0%

C陷阱与缺陷

《C陷阱与缺陷》读书笔记,包含各个章节的主要内容。

书籍封面

词法陷阱

  • 程序中的单个字符孤立来看并没有什么意义,只有结合上下文才有意义。
  • =不同于===是赋值运算符,其运算结果是右边表达式的取值。==是比较运算符,其运算结果是0(不等)或1(相等)。
  • &不同于&&&是位与运算符,&&是逻辑与。
  • |不同于|||是位或运算符,||是逻辑或。
  • 词法分析中的贪心法:每个符号应该包含尽可能多的字符,符号的中间不能有空白(空格符、制表符和换行符)。
    • a---ba -- - b的含义相同,而与a - -- b的含义不同。
    • 合理的利用空格和括号,进行符号分隔。
  • C语言可以表示三种字面常量,以0x开头的十六进制,以0开头的八进制,其余是十进制。注意每种进制下的有效字符集。
  • 用单引号引起的一个字符实际上代表一个整数,一般是ASCII。用双引号引其的字符串,代表的是一个指向无名数组起始字符的指针

词法陷阱

理解函数声明

利用typedef简化包含函数指针的函数声明。

运算符的优先级问题

C语言运算符优先级表:TODO

优先级从低到高依次为:

  • 数组下标、函数调用、结构成员选择,自左向右。
  • 单目运算符
  • 双目运算符
    • 算术运算
    • 移位运算
    • 关系运算:==!=低于其他关系运算符
    • 逻辑运算:从高到低依次为:& ^ | && ||
    • 赋值运算
  • 条件运算符(? :)
  • 逗号运算符

注意作为语句结束标志的分号

  • if语句和while语句的后面不要加分号
  • return语句、结构体和联合体定义的后面加分号。

switch语句

每个case语句最好都加上一个break语句,如果确实不用加,请注释说明。case作为入口,只在遇到breakswitch结束时,退出switch语句。

函数调用

在函数调用时即使函数不带参数,也应该包括参数列表。

1
f();

悬挂else引发的问题

else始终与同一对括号内最近的未匹配的if结合。

语义陷阱

本章列出了若干种可能引起歧义的程序书写方式。

指针与数组

C语言的数组应该注意以下两点:

  • C语言只有一维数组,数组大小在编译期就作为一个常数确定。数组元素可以是任何类型的对象,可以仿真出多维数组。
  • 对于一个数组,只能获取数组大小、获取指向数组下标为0的元素的指针。其余操作都是通过指针进行的。

考虑以下的数组,该数组拥有12个元素,每个元素都是一个拥有31个整型元素的数组。sizeof(calendar)的值是12*31*sizeof(int)。在其他场合,calendar会被转换成一个指向calendar数组的起始元素的指针,其类型是int(*)[31]

1
int calendar[12][31];

指针加1,表示指向下一个元素。

非数组的指针

作为参数的数组声明

使用数组名作为参数,那么数组名会立刻转换为指向该数组第1个元素的指针。

避免举隅法

复制指针并不同时复制指针所指向的数据。

空指针并非空字符串

边界计算与不对称边界

求值顺序

  • a && b 如果a为假,则b不会求值
  • a || b 如果a为真,则b不会求值
  • a ? b : c 如果a为真,只有b会求值;否则只有c会求值
  • a, b, c a、b、c顺序求值,a和b的值求值,c的值作为表达式的值

运算符&&、||和!

逻辑运算与按位运算的区别。

整数溢出

为函数main提供返回值

链接

一个C程序可能是由多个分别编译的部分组成,这些不同部分通过一个叫做链接器的程序合并成一个整体。

什么是链接器

声明与定义

命名冲突与static修饰符

static修饰符号的作用域仅在本文件。

形参、实参与返回值

检查外部类型

头文件

库函数

探讨某些常见的库函数,以及编程者在使用它们的过程中可能的出错之处。

返回整数的getchar函数

1
int getchar(void);

更新顺序文件

fwritefread之间需要调用fseek

输出缓冲与内存分配

setbuf函数。

使用errno检测错误

先检查库函数的返回值,出错之后再检查errno。

库函数signal

信号处理函数是异步执行的。要足够的简单。

预处理器

  • 修改程序中出现的所有实例。
  • 降低调用开销,

不能忽视宏定义中的空格

宏并不是函数

宏并不是语句

如果宏包括多条语句,最好用do{ ... }while(0)包裹起来。

宏并不是类型定义

可移植性缺陷

应对C语言标志变更

标识符名称的限制

整数的大小

字符是有符号整数还是无符号整数

移位运算符

  • 无符号数右移空出的位填充0,而有符号数填充0还是符号位,取决于实现。
  • 移位的位数是整数的长度。

内存位置0

除法运算时发生的截断

随机数的大小

大小写转换

首先释放然后重新分配

附录A PRINTF,VARARGS与STDARG