|||
《C语言缺陷和陷阱》中文版的读书笔记,和我关于C语言编译系统实现的一些杂乱思考及我的答案。
第一章,词法陷阱
C语言词法分析:
C语言的符号可能包含一个或多个字符。C语言编译器做词法分析时认为一个符号尽可能包含更多的字符。C语言从左到右逐个读入字符,如果一个字符可以和前面的字符组成符号,则继续读入直至不能所读字符不能与前面字符组成任何符号。因为读入的空格和换行等也是有效字符,所以C语言符号间不可有空格和换行等符号。如
num = 11; printf(); a == 1;//正确
num = 1 1; pr intf(); a = = 1;//错误。
注意因字符的结合顺序而产生的词法的二义性,如:
x / *p; //x除以指针p所指的值,
x/*p; // 其中的“/*”系统认做注释,系统会继续读入字符直至“*/”为止
注意整形常量以0为开头被认做八进制数,以0x开头被认做十六进制数字。所以010和10不同。
第二章、语法陷阱
1,函数声明
函数的类型为函数返回值类型。变量和函数的声明可以使用括号。如:
float ((f)); //f为浮点变量
float (ff()); //f为返回值为浮点数的函数
函数声明时,函数名后的括号优先级高于*,以下语句含义不同
float *g(); //相当于*(g()),g为函数,返回值为指向浮点数的指针
float (*h)(); //h为指向函数的指针,所指向的函数返回值为浮点数。
一旦知道如何声明一个指定类型的变量,那么该类型的类型转换符就很容易得到了。只要把声明中的变量名和声明末尾的分号去掉,并用括号封装起来就成为一个类型转换符,如由float (*h)()可以得到(float (*)()),表示一个“指向浮点型函数的指针”类型转换符
设fp为函数指针,调用其所指的函数使用
(*fp)();
fp是指针,*fp是该指针指向的函数,(*fp)()就是调用该函数。此语句*fp两边的括号非常重要,如果去掉则变成*fp();相当于*(fp()),ANSI C把它做为*((*fp)())的简写(what?)。
例:调用存储位置为0的子函数。方法一:声明一个指针fp,指向一个空函数。将其初始化为0,并调用
void (*fp)();
(*fp());
方法二:将常数0转换为指向返回值为void的函数的指针类型(void (*)()) 0,并调用
(*void (*)())0); //末尾的分号使表达式成为一个语句
方法三,使用typedef
typedef void (*funcptr)();
(*(funcptr)0)();
2、运算符的优先级
运算符的优先级有15个:
(1)、优先级最高的并不是真正意义上的运算符,包括数组下标、函数调用、结构成员等。
(2)、单目运算符的优先级仅次于以上运算符,他们在真正意义的运算符中优先级最高。单目运算符自右向左结合。
(3)、接下来是双目和三目运算符,优先级从高到低依次是算术运算符、移位运算符、关系运算符、逻辑运算符、条件运算符(三目)和赋值运算符。
(4)、逗号运算符优先级最低。
例,int *p;
*p++ = 100;//将100赋予*p,然后指针向后移位。
3、注意作为语句结束标志的分号
(1)、单独的分号作为一个空语句。
(2)、多写的分号有时造成Bug,如在if或while语句之后如果多的分号。相当于{}
(3)、少写的分号也会造成Bug,如在return后少写分号可能会返回return后的表达式。在结构体定义后少写分号可能将其后的main函数定义为此结构体类型。
4、注意switch语句case后的break
当没有break时,switch语句会执行被match的常量后所有语句,包括气候的case。
5、函数调用要有括号
如果f是函数,f();调用函数。而f;只是求出f的地址而没有调用。
6、悬挂else引发的问题
注意if else语句的配对。else始终与同一大括号内最近的未配对的语句配对。
第三章、语义陷阱
1、指针与数组
重要!见书。
2、字符串指针
注意字符串结尾的空字符"