C陷阱与缺陷
0x00理解函数声明
(*(void(*)())0){};
- 构造表达式的简单规则:任何 C 变量的声明都由两部分(类型 以及 一组类似表达式的声明符)
float *g(),(*h)()
// 表示 *g() 与 (*h)()是浮点表达式,因为()结合优先级高于* *g() <=> *(g())
//g 是一个函数,该函数的返回值类型为指向浮点数的指针
//h 是一个函数指针,h所指向函数的返回值为浮点类型
(float (*)())
//表示一个指向返回值为浮点数类型的函数指针
// fp是一个函数指针,如何调用 fp 所指向的函数
// 因为fp是一个函数指针,那么*fp就是该指针所指向的函数,所如下就是调用该函数的方法。
(*fp)()
0x01运算符的优先级问题
0x02注意作为语句结束标志的分号
-
C 语言中多写了一个分号可能造成的后果:
- 被视作一个不会产生任何实际效果的空语句
- 编译器因为这个分号产生一条警告信息
-
少写一个; 在 return 后面少写 ; 可能不会报错,只是会返回 return 后的值。如果之前的函数的返回值类型为 void 则会因为返回值不一致而报错。如果省略了返回值类型,则可能不会报错,因为编译器会隐含的将返回值类型视为 int 。
//例 if(n < 3) return logrec.date = x[0]
-
如果一个声明的结尾紧跟一个函数定义时,如果声明结尾的分号被省略,编译器可能会把声明的类型视作函数的返回值类型。
// 例 struct lonrec{ int date; } main(){ }
0x03switch 语句
-
C 语言根据其设计会因为 case 语句后面的 break 的有无而执行情况不同(如果没有 break 会一直执行到 break 或执行完),可以利用此特性的方便设计解决问题的程序。
// example1.c // 加减法示例,只要将第二个操作数的正负号反号后, // 减法和假发运算处理本质上就是一样的, // 这样会大大方便程序的处理 case SUBTRACT: opend2 = - opend2; /* 此处没有 break 语句*/ case ADD: ......
// example2.c // 字符统计示例,忽略空白字符(\n \t ' ') case '\n': linecount ++; case '\t': case ' ':
0x04函数调用
- 与其他程序设计语言不同,C 语言要求:在函数调用时使用函数不带参数,也应该包括参数列表。
// 因此如果 f 是一个函数: f(); // f(); 是一个函数调用语句, f; // f; 是一个什么也不做的语句。更精确地说,这个语句计算函数 f 的地址,却不调用该函数。
0x05"悬挂" else 引发的问题
// else1.c
if (x == 0)
if (y == 0)
error();
else{
z = x + y;
f(&z);
}
// else2.c
// 上边 else1.c 的代码会被编译器理解成如下缩进
if (x == 0)
if (y == 0)
error();
else{
z = x + y;
f(&z);
}
// 因为C语言中有这样的规则,
// else 始终与同一对括号内最近的未配置的 if 结合。
// else3.c
// 如果想要被编译器理解成 else1.c 缩进形式则需要写成下边形式
if (x == 0){
if (y == 0)
error();
}
else{
z = x + y;
f(&z);
}