阅读《C陷阱与缺陷》的知识增量

版权声明:本文为Focustc原创文章。转载请注明作者及出处。 https://blog.csdn.net/caozhankui/article/details/35925939

看完《C陷阱与缺陷》,忍不住要又一次翻一下,记录一下与自己的惯性思维不符合的地方。

记录的是知识的增量。是这几天的流量,而不是存量。
这本书是在ASCI C/C89订制之前写的。有些地方有疏漏。

第一章 词法陷阱

  • 1.3 C语言中解析符号时使用贪心策略,如x+++++y将被解析为x++ ++ +y。并编译出错。
  • 1.5 单引號引起的一个字符代表一个相应的整数,对于採用ASCII字符集的编译器而言,'a'与0141、97含义一致。
  • 练习1.1 嵌套凝视(如/*/**/*/)仅仅在某些C编译器中同意,如gcc4.8.2编译时是不支持的。

第二章 语法陷阱

  • 2.6 else始终与同一个括号内近期的未匹配的if结合

第三章 语义陷阱

  • 3.1 int a[12][31]表示的是一个长度12的数组,每一个元素是一个长度31的数组。

  • 3.1 在须要指针的地方假设使用数组名来替换,那么数组名就被视为其下标为0的元素的指针,p = &a的写法是非法的(gcc4.8.2仅仅是警告)。
  • 3.2 怎样连接两个给出的字符串s、t?细节非常重要,书中给出的答案例如以下:
char *r,*malloc()

//原文称不能直接声明一个s、t长度之和的数组,但c99能够声明变长数组,已经能够了
//记得要把长度加1
r = malloc(strlen(s) + strlen(t) +1);

//必须推断内存是否分配成功
if(!r){
    complain();
    exit(1);
}

strcpy(r,s);
strcat(r,t);

......

//完毕之后一定要释放r
free(r);
  • 3.6 怎样正确计算数组的边界?原则一。考虑最简单情况下的特例;原则二,细致计算边界。

  • 3.6 下面一段代码为何引起死循环?这是由于在内存地址递减时,a[10]就是i。
    int i,a[10];
    for(i = 1; i<=10; i++)
    a[i] = 0;
  • 3.6 边界的编程技巧:用第一个入界点和第一个出界点表示数值范围,即[low,high)。

    这种效果是

    • 取值范围的大小为两者之差。
    • 若取值范围为空,则上界等于下界。
  • 3.6 --n一般比n--执行速度更快。
  • 3.7 运算符&&和||保证两个操作数从左至右求值。其它运算符的操作数求值顺序没有定义。

    比方y[i] = x[i++]结果是没有定义的。

  • 3.9 怎样检測a+b是否溢出?
    • if(a+b < 0)是不对的,由于溢出时的行为是没有定义的。正确的方法是将两者转换为unsigned型与INT_MAX比較
    • 更巧妙的方法:if(a > INT_MAX - b)

第四章 连接

  • 4.2 int a若出如今全部函数体之外,则完毕了声明与定义(分配存储空间)。

    extern int a;仅仅是声明。说明a的存储空间是在其它地方分配的,不是定义;因此必须在别的某个地方定义。同一个或不同的源文件均可。

  • 4.3 static修饰符能够将一个函数或变量的作用域限制在一个源文件之内。不会与其它文件里的同名量发生冲突
  • 4.5 声明与定义必须严格同样。而数组和指针是不同的。
  • 4.6 怎样避免声明与定义不符?遵守“每一个外部对象仅仅在一个地方声明”的规则就可以。一般放在头文件里,全部用到此外部对象的源文件都要包含此头文件,定义此对象的文件也应该包含此头文件。

第五章 库函数

  • 5.1 getchar()返回整数,不能把返回值赋值给char型变量再与EOF比較,由于EOF定义为-1,应该赋值给int型变量。
  • 5.2 假设要对文件进行连续的read和write操作,则中间必须插入fseek函数调用。
  • 5.3 setbuf(stdout, buf);能够强制将buf指向的char数组设为缓冲区,改变输出缓存大小。
  • 5.3 书中使用缓冲区把stdin的内容拷贝到stdout的程序是错误的,由于缓冲区内容的写出直到缓冲区满或调用fflush才開始完毕。

    能够把buf声明为静态的或者malloc在堆中,防止main函数结束后buf清空。

  • 5..1 一个程序异常终止时,程序输出的最后一部分经常丢失,能够使用setbuf指向一个空指针作为缓冲区
  • 5..2 putchar/getchar在stdio.h中使用宏实现,假设没有包含stdio.h。非常大可能仍能执行,可是使用相应的函数取代,速度减少。

第六章 预处理器

  • 6 宏仅仅是对文本处理,是一个表达式,不是函数或语句
  • 6.1 宏定义最好把每一个參数和整个表达式使用括号括起来防止出错。

  • 6.2 假设一个操作数在两个地方用到。将被求值两次。解决方式:操作数应该没有副作用;将宏实现为函数。
  • 6.2 宏可能产生非常庞大的表达式。

  • 6.3 宏的分号的使用非常麻烦,assert的一种正确实现:#define assert(e) ((void)((e)||_assert_error(__FILE__,__LINE__)))
  • 6.4 typedef struct foo FOOTYPE是类型定义语句,定义了一个新的类型。

第七章 可移植性缺陷

  • 7.4 编译器实现可能将字符当作有符号或无符号的。

    char转换为int时结果没有定义。能够使用unsigned char避免。

  • 7.4 将字符变量转换为无符号整数时应该使用(unsigned char)c而不是(unsigned)c,后者将c转换为int再转换为unsigned int。

  • 7.5 除法运算速度大大慢于移位。

  • 7.7 整数除法运算时。仅规定商 x 除数 + 余数 == 被除数。大多数实如今负数的除法时。仅仅保证余数与被除数正负号同样,商与被除数的符号无关。应尽量使n为无符号数。
  • 7.9 toupper/tolower函数均採用int型參数,实现时要检查输入是否符合要求。採用置位实现非常高速。
  • 7.11 要求一个按位输出long型数字。须要考虑:不能对-n求值。可能溢出(边界条件),应该把n转换为负的再处理;余数的符号未知。应做归一化处理。

  • 7..2 atoi函数把字符串转换为long型整数。应该依照负数来处理以避免溢出。

  

转载请注明作者:Focustc,博客地址为 http://blog.csdn.net/caozhk,原文链接为 点击打开

猜你喜欢

转载自www.cnblogs.com/mqxnongmin/p/10666564.html