第一章 词法陷阱
= 别写成== 了,同样| &等都要注意。
赋值后,并检查变量是否为零时,要明确写出判断语句,其实是判断语句最好都明确写出来!
if (x = y)
foo();
应该写为:
if ((x = y) != 0)
foo();
尽量不要写出:
if (x)
foo();
把判断都写清楚:
if (x != 0)
foo();
C编译器词法分析很多采用“大嘴法” (重要)
既是说,读入一个符号后,继续读取下一个符号,如果能和下一个符号一起组成一个有意义的“词组”,那就再继续读取下一个符号,直到没有下一个符号,或者前面的“词组”和下一个符号无法复合出一个有意义的“词组”为止。
举例,a---b,编译器理解的时候,先读一个-,然后看下一个符号还是-,能组成--的“词组”,于是继续读,发现又是个-,而---没有实际意义,于是编译器把上面那句话理解为:(a--) - b
注意:有意义的“词组”中间,是不能加入空格的!上面例子写为 a - - - b,就要报错了。
再举例,y = x/*p /*p指向除数*/
这个编译器理解的时候,先读/,然后继续读*,得到"词组" /*,发现有意义,再往下读就没符号了,于是把上面的语句理解为 y = x 然后 后面都是注释。
但是!如果加入空格,变成y = x/ *p,编译器就能按照我们想的方式去理解。因为 / 和 * 之间有了空格,就无法组成词组。
注意:上面语句的写法就不好,更好更清楚的应为y = x / (*p);就没烦恼了。
问题:a+++++b怎么解释?
答:( ( a++ ) ++ ) + b。小心为上,感觉面试题会出这么二问题。
整型常量别为了对齐或什么原因,开头乱加个0
举例:
struct {
int part_number;
char *description;
}parttab[] = {
046, "left-handed widget" ,
047, "right-handed widget" ,
125, "frammis"
};
即忘记了整型常量开头加零就成八进制了。
总之就是别粗心,认真第一。
注意字符串和字符区别
printf("Hello world\n");
和
char hello[] = {'H','e','l','l'……};
printf("%s\n",hello);
显示效果一样,但是显示出来的Hello world存的位置不一样,一个在栈,一个在静态存储区。
作者这里写的printf(hello);但是我自己编译了一下,编译不过去,说hello不是string literal。
单引号相当于整数值
(重要)
int a;
a = "yes";
这是把"yes"字符串所在内
存的首地址,存给了a。
a = 'yes';
赋值后,a的值会因不同的编译器,而有所不同。在Borland下,a = 'y'的值,后续的e s都忽略;而VC和
GCC中,是a = 's' 即一个个覆盖前面一个值。
注意:我写的a = 'y' a = 's' 意思是 y 和 s 的值,不是字符。
第二章 语法陷阱
理解函数声明
(重要)
把变量的类型声明看作两部分,一部分是类型,另一部分是个表达式,对这个表达式求值后,得到的值是前面那类型。
比如:
float f,g;
float就是类型,后面f g就是两个表达式,表达式单独拿出来求值后,值的类型就是float。
同理,float ff();把表达式单独拿出来,ff()求值后,值的类型是float。ff是函数。
float *pf;也可以这么解释其意义。
于是,麻烦点的,float *g(), (*h)();
表达式*g()求值后是个float值,()优先级比*高,所以表达式等效为*(g()),表示g是个函数,g()是执行这个函数后,对返回的值进行解引用,再得到的值是float。即,g是个返回值为指向float类型值的指针。
表达式(*h)()中,*h被()括起来,那先看*h,表示h是个指针,解引用后的值是个函数*h,对这个函数求值(*h)(),再得到的值是个float类型值。即,h是个指向返回值为float类型的函数的指针。
把分号,和变量名去掉,再在外面用括号封装起来,就是类型转换符。
float (*h)();中,h是个指向返回值为float类型的函数指针;则(float (*)())就是“指向返回值为float类型的函数的指针”的类型转换符。
问题:(*(void(*)())0)()是什么意思?
答:首先看最外层,是两个括号(*xxxxx)(),优先级相同,说明这是个求函数指针值的表达式。这个函数指针就是里面那个xxxxx,即(void(*)())0。0是NULL指针,根据上面讲的类型转换符,知道(void(*)())是个类型转换符,给他去掉封装的括号,加上变量名和分号后为:void (*h)(); h是指向返回值为void的函数的指针。
用typedef整理一下就好明白多了:
typedef void (*funcptr)();
(*(funcptr)0)();就是上面那个(*(void(*)())0)()。
不要混合运用算术运算符和逻辑运算符
举例:
r = hi << 4 + low;
如果你想的是先执行hi << 4 ,再执行+ low,那就错了。因为<<比+优先级低。
解决方法两种:
加括号 r = (hi << 4) + low;
纯用逻辑运算符 r = hi << 4 | low ;
第二种方法要好些,因为这里是简单的表达式,复杂一点的话大括号就太乱了。
优先级顺序
(重要)
优先级一般都不要求记忆,也即不住,但大体规律有所了解也是好的。