当前位置:首页 > c陷阱与缺陷学习笔记
#include
void main(void) {
int c;
char buf[BUFSIZ]; setbuf(stdout,buf);
while((c = getchar()) != EOF) putchar(c); }
这个是不对的。buf最后一次被清空是在什么时候?答案是在main函数结束之后,作为程序交回控制给操作系
统之前C运行时库所必须进行的清理工作的一部分。但是在此之前buf已经被释放。
解决方法一是加上static 声明。也可以把buf声明完全移到main函数之外。第二种办法是动态分配缓冲区,
在程序中并不主动释放分配的缓冲区
5.4 使用erron检测错误
很多的库函数,特别是那些与操作系统有关的,当执行失败时会通过一个名称为errno的外部变量,通知 程序该函数调用失败。
下面的是错误的: /*调用库函数*/ if(errno)
/*处理错误*/
因为,在库函数调用没有失败的情况下,并没有强制要求库函数一定要设置errno为0,这样errno的值可能
就是前一个执行失败的库函数设置的值。 下面更正了,可还是错误的: errno = 0; /*调用库函数*/ if(errno) /*处理错误*/
库函数在调用成功时,既没有强制要求对errno清零,但同时也没有禁止设置errno。
下面才是对的:
/* 调用库函数 */ if(返回的错误值) 检查errno
5.5 库函数signal
从理论上说,一个信号可能在C程序执行期间的任何时刻上发生,甚至可能出现在某些复杂的库函数(如
malloc)的执行过程中。因此从安全的角度讲,信号的处理函数不应该调用上述类型的库函数。基于同样的原
因,从signal处理函数中使用longjump退出,通常情况下也是不安全的:因为信号可能发生在malloc 或者其它
库函数开始更新某个数据结构,却又没有最后完成的过程中。因此signal处理函数能够做的安全的事情,似乎
就只有设置一个标志然后返回,期待以后主程序能够检查到这个标志,发现一个信号已经发生。
然而,就算这样做也并不总是安全的。当一个算术运算错误引发一个信号时,某些机器在signal处理函
数返回后还将重新执行失败的操作。因此对于算术运算错误,signal处理函数的惟一安全、可移植的操作就是
打印一条出错消息,然后使用longjump或exit立即退出程序。
当一个程序异常终止时,程序输出的最后几行常常会丢失,原因是缓冲。
Chapter 6 预处理器
6.1 不能忽视空格
6.2 宏并不是函数
6.3 宏并不是语句
#define assert(e) ((void)((e)||_assert_error(_FILE_,_LINE_))) 6.4 宏并不是类型定义
我们没有办法在一个C表达式的内部声明一个临时变量。 避免副作用的一个办法就是再引入一个变量。
在某个上下文中本应需要函数而实际上却用了函数指针,那么该指针所指向的函数将会自动地被取得并替换这 个函数指针。
Chapter 7 可移植性缺陷
7.1 应对C语言标准变更 7.2 标识符名称的限制
c标准所能保证的只是,c实现必须能够区别出前6个字符不同的外部名称,且并没有要求区分大小写。 7.3 整数的大小
一个普通(int)整数足够大以容纳任何数组下标。
字符长度由硬件决定
7.4 字符是有符号整数还是无符号整数
若为有符号,则将其转为int时,应该同时复制符号位,而无符号,则填 0即可。 一个常见的错误是:如果c是一个字符变量,使用(unsigned)c就可得到与c等价的无符号整数。这是错误
的,因为在将字符c转换为无符号整数之前,c将先被转为int型,而此时可能得到非预期的结果。
正确的是使用语句(unsigned char)c,这样就直接转换。 7.5 移位运算符
如果被移位的对象长度是n位,那么移位计数必须大于或等于0,而严格小于n。
即使某些c实现将符号位复制 到空出的位中,有符号整数的向右移位运算也并不等于除以2的某次幂。
(-1)>>1这一般不可能为0,但(-1)/2一般为0.
7.5 内存位置0
NULL指针并不指向任何对象,只能用于赋值或比较运算。
7.7 除法运算的截断
q = a / b; r = a % b;
C 语言的定义只保证q*b+r==a,以及当a>=0且b>0时,保证|r|<|b|以及r>=0.最好避免a为负值。
7.8 随机数的大小
RAND_MAX
7.9 大小写转换
7.10 首先释放,然后重新分配
注意早期的C实现可以realloc一个已经free了的指示针。
7.11 一个例子
因为字符串常量可以用来表示一个字符数组,所以在数组名出现的地方都可以用字符串常量末端替换。 如:
\
-n可能溢出,因为最小负数的绝对值大于最大正数的绝对值。所以改亦正数的符号不会有问题,而改变
负数的符号则可能有问题。
void printnum(long n, void (*p)()) {
if(n<0) {
(*) ('-'); n=-n; }
if(n>=10)
printnum(n/10,p);
(*p)((int)(n) + '0'); }
上面的是有问题的。下面的才是对的: void printneg(long n, void (*p)()) {
long q; int r;
q = n / 10; r = n % 10; if(r>0) {r -= 10; q++; } if (n <= -10) printneg(q,p);
(*p)(\}
void printnum (long n, void (*p)()) {
if(n < 0) {
(*p)('-'); printneg(n,p); } else
printneg(-n,p);
共分享92篇相关文档