本章问题

1.预处理器定义了5个符号,给出了进行编译的文件名、文件行的当前行号,当前日期和时间以及编译器是否为ANSI C编译器。为每个符号举出一种可能的用途。

answer:在打印错误信息时,文件名和行号可能是很有用的,尤其是在调试的早期阶段。事实上,assert宏使用它们来实现自己的功能。_DATA_和_TIME_可以把版本的信息编译到程序中。最后,_STDC_可以用于条件编译中,用于在必须由两种类型的编译器进行编译的源代码中选择ANSI C和前ANSI的结构。

2.说出两个使用#define定义的名字代替字面值常量的优点。

answer:First,a well chosen name gives the reader some idea of the meaning of a quantity,whereas a literal constant conveys only its value,Second,literal constants that are used more than once are easier to change if they are defined in a single place.

(首先,一个合适的名字能给读者更多关于数量的信息,然而一个字面值常量仅仅传达它的值,其次,定义在一个地方的字面值常量在多次出现时更容易更改)

3.编写一个用于调试的宏,打印出任意的表达式。它被调用时应该接受两个参数。第一个是printf格式码,第二个是需要打印的表达式。

answer:

#define PRINT(format, value) \
printf("file: %s,line: %d, %s = "format"\n",\
__FILE__, __LINE__,#value, value);
//调用方式
DEBUG_PRINT("%d", x * y + ); //输出
file: main.c, line: : x * y + = -

4.下面的程序将打印出什么?在展开#define内容时必须非常小心!

#define MAX(a,b) (a)>(b)?(a):(b)
#define SQUARE(x) x*x
#define DOUBLE(x) x+x main()
{
int x,y,z;
y = , z = ;
x = MAX(y,z);
printf("%d %d %d\n",x, y, z); y = , z = ;
x = MAX(++y, ++z);
printf("%d %d %d\n",x, y, z); x = ;
y = SQUARE(x);
z = SQUARE(x+);
printf("%d %d %d\n",x, y, z); x = ;
y = ;
z = MAX(*DOUBLE(x),++y);
printf("%d %d %d\n",x, y, z);
}

answer:

3 2 3

5 3 5

2 4 20

2 4 12

5.putchar函数定义于文件stdio.h中,尽管它的内容比较长,但它作为一个宏实现,你认为它为什么以这种方式定义?

answer:Because putchar is invoked(调用) so often,speed of invocation was considered of primary importance,Implementing it as a macro eliminates the overhead of a function call.

(因为puchar被调用的次数比较多,调用的速度是考虑的一个重要因素,执行宏比函数需要的开销更小)

6.下列代码是否有错?如果有,错在何处?

answer:我们无法通过给出的源代码判断进行判断。如果process以宏的方式实现,并且对它的参数求值超过一次,增加下标值的副作用可能会导致不正确的结果。

7.下列代码是否有错?如果有,错在何处?

answer:这段代码有几个地方存在错误,其中几处比较微妙。它的主要问题是这个宏具有依赖于副作用(增加下标值)的参数,这种依赖性是非常危险的,由于宏的名字并没有提示它实际所执行的任务(这是第二个问题),这种危险性进一步加大了,假定循环后来改为:

尽管看上去相同,但程序此时会失败。最后一个问题是:由于宏始终访问数组中的两个元素,所以如果SIZE是个奇数值,程序就会失败。

(为什么代码改为上述代码,程序还是会失败呢?不是已经没有副作用了吗?首先我们来看看这个程序的功能“Sum all the value in the array”,意思是计算数组的所有值的总和,源代码的意思是每次添加两个连续数组的值,也就是SUM(value) 的意思就是要计算 array[i] + array[i+1]的值,所以说“依赖”于宏的副作用,它就是故意利用宏的副作用来实现,把代码改掉之后,可以把宏直接去掉计算数组总值)

8.下列代码是否有错?如果有,错在何处?

在文件header1.h中

在文件header2.h中

answer:

answer:Nothing is wrong,They each include the other,and it first appears that the compiler will read them alternately until its include nesting limit is reached.In fact this does not happen because of the conditional compilation directives,Whichever file is included first defines its own symbol and then causes the other to be included,When it tries to include the first one again,the entire file is skipped.

(没有错,它们互相包括对方,它第一次出现编译器将交替读取它们知道嵌套达到限制,事实上这并不会发生因为条件编译指令,任何一个文件在第一次定义时产生了一个标志符号,所以导致在其他文件中再次被包含时会跳过它)

9.在一次提高程序可读性的尝试中,一位程序员编写了下面的声明。

这段代码是否有错?如果有,错在何处?

answer:

Unfortunately,sizeof is evaluated(求值) after the preprocessor(预处理器) has finished its work,which means that this won't work,An alternate approach would be use the values defined in the limits.h include file.

(很不幸,sizeof的求值是在预处理器完成工作之后,这意味着这个程序将不会工作,一个可用来代替的方法是使用limits这个头文件来判断值)

其中sizeof(int) == 2这个表达式是在判断int的值是否是两个字节,在limits这个头文件中定义了类型的大小,int值在INT_MIN到INT_MAX之间,所以可以用limits.h中定义的值来代替sizeof这个语句

本章练习

1.你所在的公司向市场投放了一个程序,用于处理金融交易并打印出它们的报表。为了扩展潜在市场,这个程序以几个不同的版本进行销售,每个版本有不同选项的组合--选项越多,价格就越高,你的任务是为一个打印函数实现代码,这样它可以很容易进行编译,产生程序的不同版本。你的函数名是print_ledger。它接受一个int参数,没有返回值,它应该调用一个或多个下面的函数,具体取决于该函数被编译时哪个符号(如果有的话)被定义。

每个函数都接受单个int参数,把你收到的值传递给你该调用的函数。

answer:

void print_ledger(int arg)
{
#if OPTION_LONG
print_ledger_long(arg);
#elif OPTION_DETAILED
print_ledger_detailed(arg);
#else
print_ledger_default(arg);
#endif
}

如果你也像我一样写出上述代码的话,那么,你也跟我一样,没看懂题目昂,因为两个选项都可能被选择,这种可能性说明不能使用#elif指令,让我们看正确答案

void print_ledger(int arg)
{
#ifdef OPTION_LONG
#define OK 1
print_ledger_long(arg);
#endif #ifdef OPTION_DETAILED
#define OK 1
print_ledger_detailed(arg);
#endif #ifndef OK
print_ledger_default(arg);
#endif
}

2.编写一个函数,返回一个值,提示运行这个函数的计算机类型。这个函数将由一个能够运行与许多不同计算机的程序使用。

我们将使用条件编译来实现这个魔术,你的函数应该叫做cpu_type,它不接受任何参数,当你的函数被编译时,在下面表中“已定义”列中的符号之一可能会被定义,你的函数应该从“返回值”列中返回对应的符号,如果左边列中的所有符号均未定义,那么函数就返回CPU_UNKNOWN这个值,如果超过一个符号被定义,那么其结果就是未定义的。

“返回值”列中的符号将被#define定义为各种不同的整形值,其内容位于头文件cpu_type.h中。

answer:

#include <stdio.h>
#include "cpu_type.h" int cpu_type(void)
{
#if defined(VAX)
return CPU_VAX;
#elif defined(M68000)
return CPU_68000;
#elif defined(M68020)
return CPU_68020;
#elif defined(I80386)
return CPU_80386;
#elif defined(X6809)
return CPU_6809;
#elif defined(X6502)
return CPU_6502;
#elif defined(U3B2)
return CPU_3B2;
#else
return CPU_UNKNOWN;
#endif
}

《C与指针》第十四章练习的更多相关文章

  1. C和指针 第十四章 预处理器 头文件

    编写一个C程序,第一个步骤称为预处理,预处理在代码编译之前,进行一些文本性质的操作,删除注释.插入被include的文件.定义替换由#define定义的符号,以及确定代码的部分内容是否应该按照条件编译 ...

  2. C和指针 第十四章 习题

    14.1 打印函数 #include <stdio.h> void print_ledger_long(){ printf("function print_ledger_long ...

  3. C和指针 (pointers on C)——第十四章:预处理器

    第十四章 预处理器 我跳过了先进的指针主题的章节. 太多的技巧,太学科不适合今天的我.但我真的读,读懂.假设谁读了私下能够交流一下.有的小技巧还是非常有意思. 预处理器这一章的内容.大家肯定都用过.什 ...

  4. 《Linux命令行与shell脚本编程大全》 第十四章 学习笔记

    第十四章:呈现数据 理解输入与输出 标准文件描述符 文件描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误 1.STDIN 代表标准输入.对于终端界面 ...

  5. JavaScript高级程序设计:第十四章

    第十四章 一.表单的基础知识 在HTML中,表单是由<form>元素来表示的,而在javascript中,表单对应的则是HTMLFormElement类型.HTMLFormElement继 ...

  6. C++ Primer Plus学习:第十四章

    第十四章 C++中的代码重用 包含对象成员的类 将类的对象作为新类的成员.称为has-a关系.使用公有继承的时候,类可以继承接口,可能还有实现(纯虚函数不提供实现,只提供接口).使用包含时,可以获得实 ...

  7. 【C++】《C++ Primer 》第十四章

    第十四章 重载运算与类型转换 一.基本概念 重载运算符是具有特殊名字的函数:由关键字operator和其后要定义的运算符号共同组成.也包含返回类型.参数列表以及函数体. 当一个重载的运算符是成员函数时 ...

  8. perl 第十四章 Perl5的包和模块

    第十四章 Perl5的包和模块 by flamephoenix 一.require函数  1.require函数和子程序库  2.用require指定Perl版本二.包  1.包的定义  2.在包间切 ...

  9. Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  10. CSS3秘笈复习:十三章&十四章&十五章&十六章&十七章

    第十三章 1.在使用浮动时,源代码的顺序非常重要.浮动元素的HTML必须处在要包围它的元素的HTML之前. 2.清楚浮动: (1).在外围div的底部添加一个清除元素:clear属性可以防止元素包围浮 ...

随机推荐

  1. git 小结

    git cherry-pick de0ec64  可将另一个分支中的提交 cherry-pick到当前分支来

  2. extjs之apply

    ext.apply(Ext.Form.VTypes,{}) {}里面的内容如下: { password:function (val.field) { if(field.initialPassField ...

  3. 使用NSJSONSerialization将数组或字典转为字符串

    IOS中将数组或字典转为字符串可以用NSJSONSerialization,代码如下: NSData* data = [NSJSONSerialization dataWithJSONObject:a ...

  4. VM virtuaBox异常关机启动不了的解决方案

    事件回放 我的物理机是win7,上面装了一个VM virtualBox,用来装Centos,有天物理机非正常关闭,导致VM virtuaBox异常关机启动不了,如下: 确实找不到这个vm_liang. ...

  5. 在Eclipse for mac中配置tomcat,使web项目自动部署到tomcat

    jdk.tomcat的配置就不多说了,网上一大堆. 一.发现问题 在eclipse中新建Dynamic Web Project,配置好本地的tomcat并写好代码后选择Run on Server,但运 ...

  6. java第一天学习作业及答案

    作业一 一.选择题 1.选出在java中有效的注释声明(AD)(选两项) A.//这是注释 B.*/这是注释*/ C./这是注释 D./*这是注释*/ 2.在控制台运行一个java程序,使用的命名正确 ...

  7. GreenDao数据库框架的配置与增删改查

    并非原创,原创地址http://blog.csdn.net/njweiyukun/article/details/51893092 配置-------------------------------- ...

  8. Android Design Support Library使用详解

    Android Design Support Library使用详解 Google在2015的IO大会上,给我们带来了更加详细的Material Design设计规范,同时,也给我们带来了全新的And ...

  9. GIS的发展

    2016年2月15日,博客园的blog申请成功了,这是我的第一篇随笔,发一个GIS的发展的视频链接 http://buluo.qq.com/p/detail.html?bid=15060&pi ...

  10. redis基础总结

    Redis 数据类型: String Hash String类型: 一个key对应一个value,二进制安全的. set方法:设置对应值的value set name value get方法:获取对应 ...