2.1 理解函数声明
这一部分是这章的难点,也是容易迷惑的地方。
声明变量float f,g;float ((f)); 其含义是((f))的类型是浮点类型,()不影响类型,所以f也是浮点类型。float *pf; 这个声明的含义是*pf是一个浮点数,所以pf是一个指向浮点数的指针。float *g(), (*h)(); g是一个函数,该函数的返回值类型为指向浮点数的指针。h是一个函数指针,h所指向函数的返回值为浮点类型。
一旦知道了如何声明一个给定类型的变量,类型的类型转换符就容易得到:只需要把声明中的变量名和声明末尾的分号去掉,再将剩余部分用一个括号封装起来即可。如:float (*h)(); 表示h是一个指向返回值为浮点类型的函数指针,(float (*)())表示一个“指向返回值为浮点类型的函数的指针”的类型转换符。
所以,开头那个(*(void(*)())0)()就好理解了。中间的(void(*)())为类型转换符,将常数0转型为“指向返回值为void的函数的指针”类型。之后(*fp)()就是函数调用,其中fp为(void(*)())0。
对于后面的signal库函数声明的讲解,我觉得该书翻译的前后不一样。比如前面还说signal返回值类型为void,后面又说该函数返回值为函数指针,这点极易让读者迷惑。经过网上查阅我是这样理解的。首先,写出该函数的声明:void ( *signal( int, void (*)( int ))) ( int ); 从里往外讲,void (*)( int )为用户定义的函数指针类型,其为signal的函数的第二个参数,所以signal( int, void (*)( int ))为signal函数的函数调用,且返回类型为函数指针,指向的函数接受一个整型参数 且没有返回值,其实就是指向它第二个参数函数的指针。因为我们声明函数指针是void (*sfp)(int);,所以signal函数的声明就是void ( *signal( int, void (*)( int ))) ( int );
2.2 运算符的优先级问题
优先级规则:
优先级最高的包括:数组下标、函数调用操作符各结构成员选择操作符。结合规则是自左于右结合。例如a.b.c含义是(a.b).c而不是a.(b.c)。
单目运算符的优先级仅次于上面。而且结合规则是自右至左结合。比如*p++就是*(p++)。
接下来是双目运算符。其中,算术运算符的优先级最高,移位运算符次之,关系运算符再次之,接着逻辑运算符,赋值运算符,最后条件运算符。
最重要两点:
1.任何一个逻辑运算符的优先级低于任何一个关系运算符。
2.移位运算符的优先级比算术运算符要低,但是比关系运算符要高。
关系运算符中==和!=优先级低于其他的关系运算符的优先级。
我认为最重要的是赋值运算符在常用运算符中是最低的,这个容易引起错误。
2.3 注意作为语句结束标志的分号
结束标志的分号有时候会引起不可预料的问题。书上我觉得比较有用的是
struct logrec{
}
main(){
}
第一个}后面的;号丢了则会跟main联系起来,要求main函数的返回值是结构logrec类型。
2.4 switch语句
C语言的case只是一个标号,需要在每个case后面加上break;来结束整个switch过程。这样做有自己的优点。比如:一个编译器在查找符号时跳过程序中的空白字符。
case ‘\n':
linecount++;
/* 此处没有break语句 */
case '\t':
case ' ':
2.5 函数调用
f();是一个函数调用语句。而f;是一个什么也不做的语句。
2.6 “悬挂”else引发的问题
C语言中else始终与同一个对括号内最近的未匹配的if结合。