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

**第一章:词法‘陷阱’**
    1.1  
=为赋值操作,==是比较,容易将=与==弄混而出现错误。
    1.2  
&和|为位运算符,&&和||为逻辑运算符。
    1.3  
贪心法:如果(编译器的)输入流截止至某个字符之前都已经被分解为一个个符号,那么下一个符号将包括从该字符之后可能组成一个符号的最长字符串。
    y=x/*p 实际等于 y=x      因为/*将后面当成了注释。
    应该写成y=x/(*p)


 **第二章:语法‘陷阱’**
    2.1 
 (*(void(*)())0)() 的意思是调用(void(*)())0所指向的函数,而后者的意义是将常数0转型为“指向返回值为void函数的指针”。
    signal函数:void(*signal(int,void(*)(int)))(int);
    2.2 
C语言运算符优先级表格。


**第三章:语义‘陷阱’**
    3.1 
int calendar[12][31]:
    calendar是一个数组,该数组拥有12个数组类型的元素,其中每个元素都是一个拥有31个int类型元素的数组。
    calendar[n]表示calendar中第n+1个拥有31个int类型元素的数组首个元素的地址。
    3.2
 将字符串s、t连接成单个字符串r:
    char *r,*mallic();
    r=malloc(strlen(s)+strlen(t)+1);  //+1为加字符‘\0’
    if(!r){                                               //判断*r所指空间是否为空
            complain();
            exit(1);
    }
    strcpy(r,s);
    strcat(r,t);
    free(r);                                           //释放r分配的内存
    3.4 
 “举偶法”:以偏概全,以全覆偏。
    指针复制只复制地址,不复制数据内容。
    3.6 
 int i,a[10];
    for(i=1;i<=10;i++)
    a[i]=0;
    死循环的原因是数组a不存在下标为10的元素,当i=10时循环体将并不存在的a[10]设为0,其实质就是讲i设置为0,因此进入死循环。
    3.7 
 i=0;
           while(i           y[i]=x[i++] 或者 y[i++]=x[i];
     不能正常工作,因为有可能在i自增之前被求值。
    3.9 
 整数溢出只发生在操作数都是有符号数的情况下。
    3.10 

main函数返回值为0表示程序执行成功,返回非0表示程序执行失败。

**第四章:连接**
         因为编译器一般只处理一个文件,所以它不能检测出那些需要一次了解多个源程序文件才能察觉的错误。
         4.2  
extern int a 是对一个外部变量a的引用,而不是对a的定义。
         4.3
  两个具有相同名称的外部对象实际上是同一个对象。
         只要所有的同名函数都被定义为static或者只有一个函数不是static,可以避免可能出现的命名冲突。
         4.4
《C陷阱与缺陷》读书笔记_第1张图片
本题由于scanf类型%d为整数型,而读入的是字符,讨论的是空间大小不对应所产生的问题,不同的编译器可能产生不同的结果。而书中结果为000001234的原因是因为字符存在于整型数的低端位,故每次读入都会将i低端位置0,而i高端位本身就是0,所以相当于i一直为0,直到文件结束位置,scanf不再读入字符c,i才能递增输出。
4.5
不同的文件中声明或定义同名称不同类型的变量会引发错误,但也有可能存在误打误撞的结果正确。
4.6
解决上述问题最好的办法是:变量只在一个文件中声明,其他文件引用都需要调用此头文件来引用。


练习:
《C陷阱与缺陷》读书笔记_第2张图片
有的编译器可能输出为%g。
原因:C语言实现中存在两种printf。
一:实现了浮点格式%e %f %g 。 
二:没有实现浮点格式。

此代码既没有math.h头文件,也未声明sqrt函数,所以程序判定使用没有浮点格式的printf来节省空间和程序大小。

**第五章:库函数**
5.1
《C陷阱与缺陷》读书笔记_第3张图片
程序中的c被声明为char类型而不是int类型,这意味着c无法容纳下所有可能的字符,因此某些输入合法的输入字符被“截断”。
情况一:截断后与EOF相等。
情况二:截断后不会与EOF相等,程序死循环。
情况三:返回值与EOF作比较,似乎“正常”工作。
5.2
fseek函数要求第二个参数为long类型,因为int类型可能无法包含一个文件的大小。
5.3
程序输出方式:1.即时处理 2.暂存
5.4
在调用库函数时,我们应该首先检测作为错误指示的返回值,确定程序执行已失败再检查errno。


**练习:**
程序异常终止,程序输出最后几行常常丢失,原因?解决办法?
原因:一个异常结束的程序可能无法来得及清空其输出缓冲区,该输出存在内存的某个位置,但永远无法取出了。
解决:因为程序失败比运行失败早很多,调试时强制不允许进行输出缓冲。
setbuf(stdout,(char *)0);


函数调用需要较多的程序执行时间,因此getchar(),putchar()等函数一般在stdio.h等头文件中进行了宏定义,若去掉这些头文件则全部变成了函数调用,程序运行会大大减慢。

**第六章:预处理器**
6.1
宏定义中宏名与参数表之间不能有空格,而宏调用则不适用。
6.3
assert()的正确宏定义:
#define assert(e) ((void)((e)||_assert_error(_FILE_,_LINE_)))
利用||的特性。
6.4

宏定义相对于typedef有更强的可移植性。

**第七章:可移植性的缺陷**
7.2
ANSIC标准所能保证的是,C实现必须能够区别出前6个字符不用的外部名称,而且这个定义中并没有区分大写字母与其对应的小写字母。
7.3
C语言中3种不同长度的整数:short、int、long。
short<=int<=long
1.三种类型是非递减的。
2.一个普通(int类型)整数足够大以容纳任何数组下标。
3.字符长度由硬件特性决定。
ANSIC标准long型整数长度为32位,而short、int型为16位,大多数机器中字符长度为8位,若要存放大量级数值,移植性最好的办法是声明变量为long类型。
7.4
(unsigned) c无法得到等价的无符号整数,因为首先会将c转换为int型整数,可能产生非预期结果。
(unsigned char) c则无需先转int,而是直接转换。
7.5
除法运算程序运行速度比移位慢。
7.7
q=a/b;
r=a%b;
条件一:q*b+r==a。
条件二:改变a正负号,则q正负号也变,但q绝对值不变。
条件三:但b>0,则r>=0且r三个条件不可能同时成立。
大多数程序设计语言选择放弃第三条,改为余数与被除数正负号相同。
7.8
ANSIC标准中定义了一个常数RAND_MAX,它的值等于随机数的最大值。
7.9
关于toupper()和tolower():
广泛性用:
《C陷阱与缺陷》读书笔记_第4张图片
效率性:

7.10

即使一块内存被释放了,realloc函数仍可正常工作,这一实现允许在某个内存块被释放后重新分配其大小,前提是内存重分配操作执行得足够早。

**第八章**
8.1 编程建议:
1.不要说服自己相信“皇帝的新装”。 有的错误极具伪装性和欺骗性。如=和== 。
2.直截了当地表明意图。 要使用括号或者其他方式让你的意图尽可能清楚明了。
3.考察最简单的特例。
4.使用不对称边界。
5.注意潜伏在暗处的BUG。我们应该坚持使用C语言中众所周知的部分,而避免使用那些“生僻”的语言特性,大大降低遭遇编译器BUG的可能性。

6.防御性编程。 对程序用户和编译器实现的假设不要太多。

**附录A**
A.1
1、sprintf函数生成的输出数据总是以空字符收尾,如果希望在输出数据中出现一个空字符,我们可以显示的使用%c格式说明把它打印出来。
2、%s格式项所对应输出的字符串必须以一个空字符(‘\0’)作为结束标志(存在特殊例外情况)。因为printf函数要以此来定位一个字符串何时结束,如果与%s对应的字符串并不是以空字符(‘\0’)作为结束标志,那么printf函数将不断打印出其后的字符,直到内存中某处找到一个空字符(‘\0’)。
3、%g格式项用于打印那些不需要按列对齐的浮点数特别有用。它在打印出对应的数值(必须为浮点型和双精度类型)时,会去掉该数值尾缀的0,保留六位有效数字。如果要打印超过6位有效数字的数,那么会采用科学计数法。另外,如果该数的指数小于或者等于-5,%g格式项才会采用科学计数法表示。
4、%e格式项可以采用指数形式表示,且其将打印出小数点后6位有效数字。
5、%f格式项强制进制使用指数形式来表示浮点数,且小数点后保留6位有效数字。
6、%%格式项用于打印一个%字符。
修饰符:
7、如果待打印的数值不能填满位置,它的左侧就会被补上空格字符以使这个数值的宽度满足要求。 宽度修饰符绝对不会截断一个输出域。 如果一个数太大而不能被它所在的栏所容纳,那么它就会挤占同一行右侧紧邻数值的位置。
标志;
8、标识字符+的作用是,规定每个待打印的数值在输出时都应该以它的符号(正号或负号)作为第一个字符。
空白字符作为标志字符时,它的含义是:如果某数是一个非负数,就在它的前面插入一个空白字符。
标志字符#对浮点数格式的影响有两方面:一,他要求小数点必须被打印出来,即使小数点后没有数字也是如此;二,如果用于%g或%G格式项,打印出的数值尾缀的0将不会被去掉。
除了+和空白字符,其余标志字符都是各自独立的。


**附录B**
Koenig & Moo
1.避免使用指针;
2.提倡使用程序库;
3.实用类来表示概念:relaxed:

这些是博主去掉了许多书中很多基础知识后对于一些记得不牢固和不了解的知识点的笔记,发出来分享一下,《C陷阱与缺陷》是一本很好的书,想要更加深入了解的小伙伴可以自行购买阅读哦。

希望各位浏览的大佬也能有所收获! \(•ㅂ•)/♥

本博主的博客网站是:http://blog.csdn.net/qq_41866010,热烈期待各位大佬的光临。(●'◡'●)ノ♥


你可能感兴趣的:(《C陷阱与缺陷》读书笔记)