《C陷阱与缺陷》读书笔记(第3章)
3.1 指针与数组
C 语言中,数组名和指向该数组的指针是不完全等同的,数组名还可以用在 sizeof 中取得数组的大小,而如果将 sizeof 用在指针上则只会得到指针本身的大小了。
a[2] 和 2[a] 是等效的。连这样也可以 2[a+1],不过不建议使用。
int calendar[12][31]; sizeof(calendar[12]);
的结果是数组的大小,而不是指针。
二维数组指针的定义:
int (*monthp)[31]; monthp = calendar;
如果维数不同,gcc会发出警告。
3.2 非数组的指针
这一节谈到了 strcpy 和 strcat 这两个函数的问题,当然现在有 strlcpy 和 strlcat 这两个解决方案。作者也提供了利用 malloc 和 strlen 的方案:
char *r;
r = malloc(strlen(s) + strlen(t) + 1);
if (!r) {
complain();
exit(1);
}
strcpy(r, s);
strcat(s, t);
...
free(s);
3.3 作为参数的数组声明
参数声明中,数组与指针完全等效,但在定义变量时,两者时不同的。而且在声明外部变量时:
extern char *hello; extern char hello[];
这两个声明的含义是不同的。(见 4.5)
3.4 避免“举隅法”
复制指针并不同时复制所指向的数据。
3.5 空指针并非空字符串
gcc 中,如果试图对 NULL 进行读操作,会有警告信息。
3.6 边界计算与不对称边界
和书中解释的一样,gcc中给数组分配空间,是按地址递减得顺序来的,所以如下定义:int i, a[10]; a[10] 其实就是 i。
将数组上界视作某序列中第一个被占用的元素,而把下界视作序列中第一个被释放的元素。比如一个储存区的初始地址为 buffer,bufptr 为一指针,则初始化只要 bufptr = buffer 就可以了。计算已用空间,则是:bufptr - buffer, *(bufptr++) = c; 这句话也就是向序列添加一个元素,比较自然。
书上讲到 –n >= 0 比 n– > 0 要高效,但试了一下,好像不同机器结果不同,所以还是用后者吧,毕竟比较容易理解。
如果定义 int a[10],虽然 a[10] 是非法空间,但是 &a[10] 还是可以使用的,这用来判断指针是否到达边界比较有用。
被节最后的按列打印数据挺不错的。
3.7 求值顺序
C 语言对 (&&, ||, ?:, ,) 四个操作符定义了求值顺序,象 &&, !!, ?: 都是有可能有一部分不求值的。
而对于赋值运算符的求值顺序则是没有规定的,所以下面的程序是不正确的:
i = 0; while (i < n) y[i] = x[i++];
3.8 运算符 &&, || 和 !
不要与 & | 相混淆。
3.9 整数溢出
如何判断两个有符号整数相加是否溢出:
if ((unsigned)a + (unsigned)b > INT_MAX) complain(); if (a > INT_MAX - b) complain();
3.10 为函数 main 提供返回值
如果不给 main 提供返回值,可能默认会返回一个垃圾整数,但通常以返回 0 来表示正常返回,所以这样可能会被认为程序执行失败了。

Leave a Reply