C陷阱与缺陷:第五,六章
第五章 库函数
1. getchar() 的返回值
char c;
while ((c = getchar()) != EOF)
putchar (c);
上面代码运行错误的原因是将c定义为char类型,而getchar()返回的是int类型值 。EOF 是一个宏,标准规定它的值必须是一个 int 型的负数常量。通常编译器都会把 EOF 定义为 -1。问题就出在,使用 char 型变量接收 getchar 等函数的返回值会导致对 EOF 的辨认出错,它有可能把正确的数据误认为是 EOF,或者把 EOF 误认为是正确的数据。解决这个问题的方法是,将变量c定义为int型。
以上参考:http://www.wscxy.com/shosh/article.asp?id=13
2.
以下第二个fseek函数发改变了文件的状态,使得文件可以正常读取:
FILE *fp;
struct record rec;
… …
while (fread((char *) &rec, sizeof(rec), 1, fp) == 1)
{
// 对rec执行某些操作
if (//必须被重新写入)
{
fseek (fp, –(long)sizeof(rec), 1);
fwrite ((char *)&rec, sizeof(rec), 1, fp);
fseek (fp, 0l, 1);
}
}
一个输入操作不能随后直接紧跟一个输出操作,反之亦然。如果要同时输入和输出操作,必须在其中插入fseek函数的调用。
3. 缓冲输出与内存分配
下面代码中的setbuf()函数将不能正常运行:
main()
{
int c;
char buf[BUFSIE];
setbuf(stdout, buf);
while ((c = getchar()) != EOF)
putchar(c);
}
原因是setbuf()的第二个参数中的数组变量buf在main结束后,将释放内存空间。但是,程序程序交回给操作系统之前要做缓冲区的清理工作,但此时的buf已被释放。解决这一问题的两个策略是:
1). 将数组变量声明为全局的,用static修饰,或是将声明放到main()外面;
2). 将数组声明为动态内存,这样不会主动释放内存空间。
第6章 预处理器
1. 宏定义中的空格
#define f (x) ((x) - 1) // f(x) 代表 (x) ((x) - 1)
如果要将f(x)定义代表为((x) - 1),则应写为:
#define f(x) ((x) - 1)
2. 宏不是函数
在宏定义时,最好把宏定义中的每个参数都用圆括号括起来。但有时即使在表达式处加有圆括号,但仍有可能存在问题,原因是参数定义有副作用。解决这一问题的方式是,宏定义的参数没有副作用,或是不要用宏定义来现实,而是用函数现实。
另外一个危险是,宏展开可能产生非常宏大的表达式。
3. 宏不是语句
当宏定义利用if语句来定义时,宏定义中的语句不会加分号,但在调用宏时,会在语句后加一个分号。这样就有可能将if的判断忽略掉。这解这一问题的办法是,在调用宏定义时后面不加分号。
4. 宏不是类型定义
宏的一个主要优点是——可移植性!但是它不是类型,与typedef定义类型上有区别,如:
#define T1 struct foo *
typedef struct foo *T2;
T1 a, b; // a是结构的指针,b是结构,而不是指针
T2 a, b; // a, b都是结构的指针