c陷井和缺陷
文章目录
本文为"c traps and pitfalls"一书的笔记
词法陷井
- 看下面的代码:
y = x/*p; /* p points to the divisor */
本意是x除以p指向的变量,而事实上这样写/*会认为是注释的开头。 所以,双目运算符两边加空格是非常好的。
语法陷井
- !=的优先级要比&之类的逻辑运算符高,所以不要有下面的写法
if (flags & FLAG != 0)
- +号等运算符优先级要比[]{#和}的高,所以不要有下面的写法
r = h << 4 + 1
- 运算符优先级的记忆方法
- 优先级最高的是那些不是“真的”运算符:函数调用(),数组下标[],
结构体成员->和.
- 然后是一元运算符,! ~
+ – 正号负号- 指针* 类型转换(type) sizeof,
所以使用函数指针的时候,一定要这样:(*p)()。记住,所有的一元运算符都是右结合的。 所以*p++会被认为是*(p++)
- 接着是二元运算符,它们有下面的规律:
代数运算符 > 移位运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符 > 条件运算符
链接陷井
- 假设一个文件里面有下面的定义:
char filename[] = "/etc/passwd";
另外一个文件里面有下面的定义:
char *filename;
这2个变量的类型一样吗?虽然指针和数组很相似,但是它们不是同一样东西。 第一个filename,它是一个字符串数组的名字,使用这个名字会生成一个指向这个 字符串第一个字符的指针,仅在需要的时候生成,不会一直存在。 第二个filename,它是一个指针,定义时就给它分配了空间,程序员想要让它指向 哪里它就会指向哪里,如果不给它赋值,它会指向NULL
语义陷井
- 表达式的顺序陷井。看下面的例子:
#include <stdio.h>
int main()
{
int a[] = { 1, 2, 3 };
int b[] = { 10, 20, 30};
int i = 0;
while (i < 3) {
a[i++] = b[i];
}
for (i = 0; i < 3; i++) {
printf("%d\n", a[i]);
}
return 0;
}
会输出什么呢?我刚开始是这样想的,先计算右边,再赋值给左边,所以会输出 10 20 30 作者再给了一个例子:
#include <stdio.h>
int main()
{
int a[] = { 1, 2, 3 };
int b[] = { 10, 20, 30};
int i = 0;
while (i < 3) {
a[i] = b[i++];
}
for (i = 0; i < 3; i++) {
printf("%d\n", a[i]);
}
return 0;
}
会输出什么呢?根据我刚才的推理,理应数组越界的,因为赋值给最后一个元素的时候, i++,i已经超过了数组a的范围了。而事实上,结果还是 10 20 30 作者的解释是,编译器无法保证获取数组元素的操作先于i自增的操作,这是不确定的。 所以在写程序的时候,最好分开写。
库函数陷井
- getchar()函数居然返回int!下面是从<stdio.h>复制过来的代码:
/* Read a character from stdin. */
__STDIO_INLINE int
getchar (void)
{
return _IO_getc (stdin);
}
而且putchar居然也是传进int参数,返回int参数
/* Write a character to stdout. */
__STDIO_INLINE int
putchar (int __c)
{
return _IO_putc (__c, stdout);
}
预处理陷井
- 宏不是函数,看下面的例子:
#define max(a,b) ((a) > (b) ? (a) : (b))
largest = max(largest,x[i++]);
虽然,在定义宏的时候很小心,加了很多括号,不至于有表达式优先级的问题,但是 这样调用还是背离了本义,因为x[i++],会使i自加多次,因为宏只是替换,而不是 拷贝参数。
- 宏不是类型定义,看下面的例子:
#define STU struct student *
typedef struct student *STUDENT;
STU a, b;
STUDENT c, d;
后面的两条定义等价于:
struct student *a, b;
struct student *c, *d;
显然,第一条背离了本意,b定义成了结构体类型,而不是指向它的指针类型。虽然 typedef的定义看起来有点别扭,但是在实际的代码中还是要使用typedef,不要 使用宏。