C 陷阱与缺陷 笔记

时间:2022-12-28 11:12:49

C 陷阱与缺陷 笔记


引言

《C 陷阱与缺陷》是本豆瓣评价很高的一本C语言书。猛戳。关于这本书有多好就不再多说了。这里不求全面的记录全书,只是一些平时不注意和不知道的C特性。

第一章:词法"陷阱"

1.1 "&"  PK "&&"   " |"  PK  " ||"

& 和 | 是位运算符 ---> 数值  C的安位运算符包括 &,|,~。对操作数的处理方式是将其视为一个二进制的位序列,分别堆砌每个为进行操作。
&& 和 || 是逻辑运算符---> 逻辑数值(true or false) C的逻辑运算符包括 && ,|| , !。 处理的方式是将其视为“真”或“假”,通常0为假,fei0为真。
&& 和 || 对前后的表达式的处理方式是,只有第一个表达式不能确定整个&&(||)的真假时才去验证后边的表达式。 而 & 和 | 这不是,他们的前后表达式都处理(特别有什么 -- ++的时间)

1.2 词法分析中的“贪心法”

C的编译器在编译的时间,按照一个简单的规则:没一个符号应该包含尽可能多的字符,也就是说,编译器将程序分解成符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么就再读入下一个字符,判断已读入的连个字符组成的字符串是否可能是一个字符的组成部分;如果可能,还读。判断,知道不可能组成有意义的符号位置。就是说,编译器会尽量的读入多的字符来组成 符号(token)。 eg:       a---b  和 a-- - b 含义相同,而不是a- --b.       y =  x/*p  编译器会把 /* 看成注释的起始符号。改写为 y = x/(*p)  or  y = x / *p 则为正确。
Note : 在code的时间养成良好的风格是很重要的,在运算符的两侧都加空格,不仅美观还可以避免词法的“陷阱”


第二章: 语法陷阱

2.1 理解函数的声明

2.2 运算符的优先级问题

C 陷阱与缺陷 笔记



















2.3 “悬挂” else引发的问题

if (x == 0)
if (y == 0 )
error();
else{
z = x + y;
f(&z);
}
这段代码的本意是应该有两种情况,x等于0以及x不等于0.对于x等于0的情况,除非y也等于0(此时才调用函数error),否则程序不做任何处理;对于x 不等于0时,程序首先将x+y 赋值给z,然后以&z为参数调用f。然而这段代码实际上所做的的与编程者意图相去甚远。原因是,C有这样的规则:else 始终同一对括号内最近的为匹配的if结合。上段程序的实际运行是
if (x == 0){
if (y == 0 )
error();
else{
z = x + y;
f(&z);
}
}
正确的写法,用大括号吧第二个if封装起来
if (x == 0)
if (y == 0 )
error();
}
else{
z = x + y;
f(&z);
}

第三章:语意“陷阱”

3.1 指针与数组

C语言中指针和数组这连资格概念之间的联系是此密不可分的。C语言中数组的要注意的地方有两点1、C语言中只有一维数组(这很重要,是理解数组和指针之间关系的关键),数组的大小要在编译的时间就作为一个常数确定。数组元素可一存储任何类型的对象,多为数组是仿真出来的。2、对于数组我们只能做两件事:确定数组的大小,以及获得指向该数组下标为0的元素指针。(数组和指针的关系就在这)
Note:在c中我们没办法可以将一个数组作为函数参数直接传递。如果我们使用数组名来传递参数,那么数组名会立刻被转化为指向该数组的第一个元素的指针。

第四章:连接


C语言的一个重要思想就是分别编译(Separate Compilation ),即若干个源程序可以在不同的时间单独进行编译,然后在一个恰当的时间整合到一起,但是连接器和C编译器是分离的。他不能了解C的诸多细节。
extern int a; 要注意的是这并不是对a的声明,而是告诉连接器a诗歌阿爱不整形变量。这是显式的说明a的存储空间是在程序的其他地方分配的。
static 是一个能减少命名冲突的工具,static int a;  其含义和int  a 是一样的,但是a的作用域限制在一个源文件内,

第六章: 预处理器

6.1 宏要注意的地方

1、不能忽视宏定义中的空格
#define f (x)  ((x) - 1)
f 和(x)之间有空格的时间  表示  f 表示  (x) ((x)- 1)

2、宏并不是函数 3、宏不是语句3、宏不是类型定义