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

看完《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,原文链接为点击打开

阅读《C陷阱与缺陷》的知识增量,布布扣,bubuko.com

时间: 2024-08-04 16:07:36

阅读《C陷阱与缺陷》的知识增量的相关文章

读书笔记--C陷阱与缺陷(七)

第七章 1.null指针并不指向任何对象,所以只用于赋值和比较运算,其他使用目的都是非法的. 误用null指针的后果是未定义的,根据编译器各异. 有的编译器对内存位置0只读,有的可读写. 书中给出了一种判断编译器如何处理内存0的代码: 1 #include <stdio.h> 2 int main() 3 { 4 5 char *p; 6 p=NULL; 7 printf("location 0 contains: %d\n", *p); 8 9 return 0; 10

《C陷阱与缺陷》学习笔记(一)

前言和导读 "得心应手的工具在初学时的困难程度往往超过那些容易上手的工具."比较认同这句话.我至今觉得自己其实还是个刚入了门的初学者. 第一章 "词法"陷阱 由于之前学过编译原理,对编译器词法分析(主要是符号识别过程)比较了解,理解起来不困难. 在讲到"="和"=="."|"和"||"."&"和"&&"时,联想起以前见过一些

C陷阱与缺陷 1

1,符号之间的空白被忽略 符号中间不能嵌入空白 2,词法分析中的贪心法 a---b 和 a-- -b相同 和 a- --b不同 1 a=b/*p //根据贪心法 /*被解释成 注释符,便不再往下读,直到匹配 */ 1 a=b/ *p//*号优先级大于 / 故为b除以 p所指向的值 根据编程规范,方便阅读,减少出错的可能 1 a=b/(*p) 3,理解函数的声明 4,运算符的优先级 5,分号的使用 1 1 2 if() a; 1 2 if(); a; 2 1 2 3 return; a; b; 1

C陷阱与缺陷之词法陷阱

该文章及后续文章均为阅读<C陷阱和缺陷>后的读数笔记,方便以后回顾 C陷阱和缺陷电子版图书下载地址:点击打开链接 第一章词法陷阱 1.1 = 不同于 == 在C语言中,符号=作为赋值运算符,符号==作为比较运算符.一般而言,赋值运算相对比较运算出现得更频繁,因此字符数较少的符号=就被赋予更常用的含义--赋值操作.在编写程序的时候,注意不要将赋值操作写完比较相等的操作,也不要将比较操作写为赋值操作. 1.2 & 和 | 不同于 && 和 || 在C语言中,&和|为

《C陷阱与缺陷》读书笔记

<C陷阱与缺陷>读书笔记 1.编译器中的词法分析器负责将程序分解为一个个符号.C语言中,符号之间的空白 (包括Space ,Tab , Enter) 都将被忽略,但一个符号的中间不能有空白,否则可能被解释成为另一个或几个符号. 2.编译器将程序分解成符号的方法是从左到右逐个字符读入,如果该字符可能会组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分:如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已经不再可能组成一个有意义的

读书笔记--C陷阱与缺陷

要参与C语言项目,于是作者只好重拾C语言(之前都是C++,还是C++方便). 看到大家都推荐看看  C陷阱与缺陷(C traps and pitfalls),于是好奇的开始了这本书的读书之旅. 决定将书中重要的知识点和易错点记录下来方便自己复习和他人学习~~不多说了,下面开始. 第一章:词法陷阱 在C语言中,符号(程序文字)之间的空白(包括空格符.制表符.换行符)将被忽略.书中举了一例: 1 if (x > big) big = x; 2 可以写成: 3 if 4 ( 5 x 6 > 7 bi

C陷阱和缺陷整理四

1.assert宏的定义 #define assert(e) \ ((void)((e) || _assert_error(__FILE__, __LINE__))) 库里面对这个宏做了这样的定义,当宏参数(或表达式)e为真的时候由||运算符的运算规则会执行_assert_error(__FILE__, __LINE__)从而打印一条报警信息.所以整个表达式的最终会变为(void)0或者(void)1这种形式,这种形式确实有点奇怪? 系统这样定义的目的是当一个值被转换为void类型之后,没有一个

C陷阱与缺陷 2

1,数组 对数组只能进行两种操作,1确定数组的大小,2获得数组第一个元素的指针,其他的操作均是通过指针来实现的. 1 2 3 4 5 6 7 8 9 #include <stdio.h> main() {     int a[2][3]={{1,2,3},{4,5,6}};     int *p,(*q)[3];     p=a[2];     q=a; } int a[2][3]={{1,2,3},{4,5,6}}; a为一个数组(A),数组的维度为3,元素为数组(B),数组(B)的维度为4

C陷阱与缺陷 读书笔记

C陷阱与缺陷 1.  typedef用法: ①    定义一种类型别名,而不是简单的宏替换: char *pa,pb;(注意:pb并没有定义为指针,虽然你可能想这么定义它) typedef char* PCHAR PCHAR pa, pb; ②   用在旧的C代码中,帮助struct.以前的代码中,声明struct新对象时,必须带上struct,即形式为:struc结构名对象名,如: struct tagPOINT1 { Int x; Int y; }; struct tagPOINT1 p1;