C缺陷和陷阱--阅读笔记(原创)
由? 王宇 原创并发布:
?
第1章词法陷阱
??? 1.1=不同于==
??? 1.2&和|不同于&&和||
??? 1.3词法分析中的“贪心法”
??? ??? 规则:每一个符号应该包含尽可能多的字符。也就是说,编译器将程序分解成符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么在读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能,继续读入下一个字符,重复上述判断,指导读入的字符组成的字符串已不再可能组成一个有意义的符号。这个处理策略有时被称为“贪心法”或者,更口语化一点,称为“大嘴发”
??? ??? a+++++b的含义(a++)+(++b)不是((a++)++)+b原因:a++不能为左值
??? 1.4整数变量
??? ??? 141(十进制)0215(八进制)0x12(16进制);01951*8^2+9*8^1+5*8^0
??? 1.5字符与字符串
??? ??? C语言中的单引号和双引号含义迥异
第2章语法陷阱
??? 2.1理解函数声明
??? ??? 如何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符
??? ??? 一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到了:只需要把声明中的变量名和声明末尾的分号去掉,再将剩余的部分用一个括号整个“封装”起来即可
??? 2.2运算符的优先级问题
??? ??? 表2-1
??? ??? 前述运算符(()[]->.)、单目运算符、双目运算符
??? ??? 双目运算符:算术运算符、移位运算符、关系运算符、逻辑运算符、赋值运算符、条件运算符
??? 2.3注意作为语句结束标志的分号:一个声明的结束需要有一个分号,struct{};
??? 2.4switch语句
??? ??? break特性
??? 2.5函数调用:f();
??? 2.6"悬挂"else引发的问题
第3章语义陷阱
??? 3.1指针与数组
??? ??? C语言中数组需要注意两点:
??? ??? 1.C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来,然而C语言中数组的元素可以是任何类型的对象
??? ??? 2.对于一个数组,我们只能够做两件事:确定该数组的大小,以及获得指向该数组下标为0的元素的指针
??? ??? int*p;inta[10];p=a;这个*p为a[0],错误:p=&a;
??? 3.2非数组的指针
??? ??? char*r,*malloc();r=malloc(strlen(s)+strlen(t));错误原因:
??? ??? 1.malloc函数有可能无法提供请求的内存.if(!r){}
??? ??? 2.给r分配的内存在使用完之后应该及时释放,这点务必要记住。free(r);
??? ??? 3.最重要的,就是调用malloc()函数时,并未分配足够的内存。malloc(strlen(s)+strlen(t)+1);
??? 3.3作为参数的数组声明
??? ??? 我们没有办法可以将一个数组作为函数参数直接传递,被转换为指向该数组第1个元素的指针。
??? ??? charhello[]="hello";printf("%s\n",hello);等效print("%s\n",&hell[0]);
??? ??? intstrlen(ints[]){}等效intstrlen(int*s){}
??? ??? main(intargc,int*argv[]){}等效main(intagrc,int**argv){}
??? 3.4避免“举偶法”:混淆指针与指针所指向的数据???
??? 3.5空指针并非空字符串
??? ??? 编译器保证由0转换而来的指针不等于任何有效的指针
??? ??? 当常数0被转换为指针使用时,这个指针绝对不能被解除引用。换句话说,当我们将0赋值给一个指针变量时,绝对不能企图使用该指针所指向的内存中存储的内容
??? 3.6边界计算与不对称边界(待续)
??? ??? C语言中一个拥有n个元素的数组,却不存在下标为n的元素,它的元素的小标范围是从0到n-1为此
??? ??? 栅栏错误的两个通用原则:
??? ??? ??? 1.首先考虑最简单情况下的特例,然后将得到的结果外推,这是原则一
??? ??? ??? 2.仔细计算边界,绝不掉以轻心,这是原则二
??? ??? 边界编程技巧:用第一个入界点和第一个出界点来表示一个数值范围。x>=16x<38;--n>=0???
??? 3.7求值顺序???
??? ??? 即求值的副作用,C语言中只有四个运算符存在规定的求值顺序:&&||?:,
??? 3.8运算符&&、||和!
??? ??? 同按位运算符&|~互换后可以正常工作,实际上是巧合
??? 3.9整数溢出
??? ??? C语言中存在两类整数算术运算,有符号运算与无符号运算
??? ??? 无符号算术运算中,没有“溢出”一说;一个运算符是有符号,另一个是无符号,有符号的会自动转换成无符好
??? ??? 当两个操作数都是有符号整数时,“溢出”就有可能发生,而且“溢出”的结果未定义。
??? 3.10为函数main提供返回值
??? ??? return0;
第4章连接
??? 4.1什么是连接器
??? ??? C语言中的一个重要思想就是分别编译,即若干个源程序可以在不同的时候单独进行编译,然后在恰当的时候整合到一起,但是连接器一般是与C编译器分离的,它不可能了解C语言的诸多细节
??? 4.2声明与定义
??? ??? inta;位置出现在所有的函数体之外,被称为外部对象a的定义;声明并初始化,默认为0;
??? ??? externinta;a是一个外部整形变量,从连接器的角度来看,声明是一个对外部变量a的引用,而不是对a的定义
??? 4.3命名冲突与static修饰符
??? ??? static修饰符是一个能够减少命名冲突的有用工具
??? 4.4形参、实参与返回值
??? 4.5检查外部类型
??? ??? 保证一个特定名称的所有外部定义在每个目标模块中都有相同的类型,一般来说是程序员的责任
??? 4.6头文件
??? ??? 规则:每个外部对象只在一个地方声明。这个声明的地方一般就在一个头文件中,需要用到该外部对象的所有模块都应该包括这个头文件。特别需要指出的是,定义该外部对象的模块也应该包括这个头文件
第5章库函数
??? 5.1返回整数的getchar函数
??? 5.2更新顺序文件
??? ??? fopen一个文件后,不可以自由地交错进行读出和写入的操作。如果要同时进行输入和输出操作,必须在其中插入fseek函数的调用
??? 5.3缓冲输出与内存分配
??? ??? setbuf(stdout,buf);通知输入输出库,所有写入到stdout的输出都应该使用buf作为输出缓冲区,知道buf缓冲区被填满或程序员直接调用fflush,buf缓冲中的内容才实际写入到stdout中
??? ??? 注意setbufmain
??? 5.4使用errno检测错误
??? 5.5库函数signal
第6章预处理器
??? 宏作用:使多个不同变量的类型可在一个地方说明:
??? ??? #defineFOOTYPEstructfoo
??? ??? FOOTYPEa;
??? ??? 大多数C语言实现在函数调用时都会带来重大的系统开销。因此我们也许希望有一种程序块,它看上去像一个函数,但却没有函数调用的开销
??? 6.1不能忽视宏定义中的空格
??? ??? #definef空格(x)((x)-1)f宏无参数,f代表(x)((x)-1)
??? 6.2宏并不是函数
??? ??? 定义最大数值宏
??? ??? #definemax(a,b)((a)>(b)?(a):(b))缺陷如下:
??? ??? ??? max(i,i++)展开则:((i)>(i++)?(i):(i++))副作用
??? ??? 修正:
??? ??? staticinta_temp,b_temp;
??? ??? #definemax(a,b)(a_temp=(a),b_temp=(b),a_temp>b_temp?a_temp:b_temp)
??? 6.3宏并不是语句
intFun(int) { intp=10; p=fun(p); }?
??? ??? 合法的递归
??? 7.3整数的大小
??? ??? 8位char1字节
??? ??? 32位intlongfloat4字节
??? ??? 16位short2字节
??? ??? 64位double8字节
??? 7.4字符是有符号整数还是无符号整数
??? ??? 8位的取值范围:
??? ??? ??? 有符号:-128~127高1位为1是负数,0是整数
??? ??? ??? 无符号:0~255
??? ??? ??? 二进制的表示方法,同正负符号的存储不同,二进制是补码,正负符号的存储同上
??? ??? ??? ??? 反码:所有位求反
??? ??? ??? ??? 补码:反码加1
??? ??? 常见错误认识是:如果c是一个字符变量,使用(unsigned)c就可得到与c等价的无符号整数。这是会失败的,因为在将字符c转换为无符号整数时,c将首先被转换为int型整数,而此时可能得到非预期的结果
??? ??? 正确的方式:(unsignedchar)c
??? 7.5移位运算符
??? ??? 如果被移位的对象是无符号的数,那么空出的位将被0填充
??? ??? 如果被移位的对象是有符号的数,那么既可以用0填充空出的位,也可以用符号位的副本填充空出的位
??? ??? 移位计数必须大于或等于0,而严格小于n(n长度,例如intn=32)
??? 7.6内存位置0
??? ??? 在所有的C程序中,误用null指针的效果都是未定义的
??? 7.7除法运算时发生的截断
??? 7.8随机数的大小
??? ??? ANSIC标准,定义的随机数的最大值:RAND_MAX
??? 7.9大小写转换
??? ??? #definetoupper(c)((c)+'A'-'a')
??? ??? #definetolower(c)((c)+'a'-'A')
??? ??? 依赖于特定实现中字符集的性质,既需要所有的大写字母与相应的小写字母之间的差值是一个常量,ASCII字符集符合
??? 7.10首先释放,然后重新分配
??? ??? malloc()free()realloc()
??? 7.11可移植性问题的一个例子
第8章建议与答案