第六章 预处理器
定义宏,应该注意空格,括号,分号,展开后if else的层次等。总之,最好少用宏,尤其在容易引起混乱的情况下。
第七章 可移植性缺陷
可移植性
1、移位运算符
1)在向右移位时,空出的位是由0填充还是1,还是由符号位的副本填充?
如果被移位对象是无符号数,那么由0填充;如果是有符号数,那么是0或符号位的副本。
2)移位操作的位数允许的取值范围是什么?
如果被移位对象的长度是n位,那么移位计数必须大于或等于0,而严格小于n。
2、一个例子
输入一个long型整数和一个函数指针。程序把给出的long型整数转换为其10进制表示,并且对10进制表示中的每个字符都调用函数指针所指向的函数。
void
printneg( long n, void (*p)() )
{
long q;
int r;
q = n / 10;
r = n % 10;
if( r > 0 ){
r - = 10;
q++;
}
if (n <= -10)
printneg( q, p);
(*p) ( "0123456789"[ -r ] );
}
void
printnum (long n, void (*p)() )
{
if ( n < 0) {
(*p)('-');
pirntneg(n, p);
}else
printneg(-n, p);
}
移植性需考虑的地方:
1)机器的字符表不同。
2)有的机器是one's complement,有的机器是two's complement的。基于2的补码的计算机,所允许表示的附属取值范围要大于正数取值范围,所以有时取负值的运算会导致溢出。
3)各机器对取模运算的定义不同。
更多可移植性问题参见How to Write Portable Software in C。
第八章 建议与答案
将惯用的c == '\t' 写作 '\t' == c。一旦写错成=号,编译器就能检查出来。
一些习题答案:
1、写一个测试程序,无论对是否允许嵌套注释的编译器都能正确执行,但运行结果不同。
a = /*/*/0*/**/1;
允许:/* /* /0 * /* */ 1,则a = 1;
不允许:/* / */ 0 * /* */ 1,则a = 0×1 = 0。
2、(a++)++语法错误。a++的结果不能作为左值。那些可以作为左值?
3、如果一个机器的字符长度为8位。那么其整数长度很可能是16位或32位,为什么?
有些机器按字符分配内存地址。有些机器按字来对内存寻址。按字符寻址的机器效率更高更流行。然而,即使是按字符寻址的机器,字的概念在进行整数运算的时候仍是很重要的。如果一个字中包含的字符数是2的某次幂,因为乘以2的运算可以转换为移位运算。所以计算机硬件就可以很容易地完成呢个从字符地址到字地址的转换。
阅读(1322) | 评论(0) | 转发(0) |