c|《C陷阱与缺陷》第二章阅读笔记

语法“陷阱” 2.1 理解函数声明 声明类型的类型转换符的方法:只需要将声明中的变量和声明末尾的分毫去掉,再将剩余的部分用一个括号整个封装起来即可。
eg1:

float (*h)()

表示h是一个指”向返回值为float类型的函数的指针“。
因此
(float (*)())

表示一个“指向返回值为float类型的函数的指针“的类型转换符”
2.2 运算符的优先级问题 优先级最高者并不是真正意义上的运算符,包括:数组下标("[]")、函数调用操作符"()"、各结构成员选择操作符".","->"。他们都是自左向右结合。
次优先级为单目运算符。在所有真正意义上的运算符中,单目运算符的优先级做高。
!逻辑反操作符 ~对二进制数值按位取反 ++前置、后置++ --前置、后置-- -负 +正 (type)强制类型转换 *解引用操作符 &取地址 sizeof

双目运算符比单目运算符优先级低。在双目运算符中,算术运算符的优先级最高,移位运算符次之,关系运算符再次之,接下来是逻辑运算符,赋值运算符,最后是条件运算符。
算数运算符 * / % + -

移位运算符 << >>

关系运算符 > >= < <= == !=

逻辑运算符 &按位与 ^异或 |按位或 &&逻辑与 ||逻辑或

赋值运算符 = *= /= %= += -= <<= >>= &= ^= |=

条件运算符 ? :

逗号表达式 ,

2.3 注意作为语句结束标志的分号 要注意分号的书写,多写或者缺失会导致意想不到的结果。
eg1:
if(a>0); a++;

该例本意为当a>0为真时,a自加1,由于if条件后多写了一个分号,导致每次程序运行,不管a>0是否成立,a++都会执行。上面的代码实际相当于:
if (a>0) { } a++;

eg2:
if (n < 3) return logrec.data = https://www.it610.com/article/x[0]; logrec.time= x[0]; logrec.code= x[0];

此处,return后面缺少一个分号,然而这段代码编译并不会出错,只是将语句logrec.data = https://www.it610.com/article/x[0]作为return语句的操作数。上面的代码实际相当于:
if (n < 3) return logrec.data = https://www.it610.com/article/x[0]; logrec.time= x[0]; logrec.code= x[0];

当一个声明的结尾紧跟一个函数定义时,如果声明结尾的分号被省略,编译器可能将声明的类型作为函数的返回值类型。示例如下:
eg3:
struct logrec{ int data; int time; int code; } main() { ... }

程序本意是main函数的默认返回值是int,由于结构体声明后缺失了一个分号,导致main函数的返回值成为了struct logrec类型的结构。
2.4 switch语句 eg1:
switch(color) { case 1: printf("red")' break; case 2: printf("yellow"); case 3: printf("blue"); break; }

当color为1或3时,执行完成之后,程序将会跳出switch语句。当color为2时,执行完2分支之后,程序会继续执行3分支,当3分支执行完成之后,程序跳出switch语句。
2.5 函数调用 假设f是一个函数,
f();

是一个函数调用语句,而
f;

却是一个什么也不做的语句。更精确的说,这个语句计算函数f的地址,却不调用该函数。
2.6 ”悬挂“else引发的问题 eg1:
if (x == 0) if (y == 0) error(); else{ z = x + y; f(&z); }

C语言中有这样的规则,else始终与同一括号内最近未匹配的if结合。上面的代码相当于
if (x == 0) { if (y == 0) error(); else { z = x + y; f(&z); } }

【c|《C陷阱与缺陷》第二章阅读笔记】与本意相违背。在编写代码的时候应该这样编写:
if (x == 0) { if (y == 0) error(); } else { z = x + y; f(&z); }

    推荐阅读