阳光烂灿的日子

--记录所有碎碎念

C陷阱与缺陷(3)

| Comments

第三章 语义陷阱

3.1指针与数组

C语言的值得注意的两点

1.C语言只有一堆数组,而且数组的大小必须在编译期就作为一个常数确定下来。因为数组中的元素可以是任何类型的对象,因此也可以是另一个数组。所以多维数组就是这样“仿真”来的。

2.对于一个数组,我们只能够做两件事:确定数组的大小,以及获得指向该数组下标为0的元素的指针。其它有关数组的操作都是通过指针进行的。

*a是数组a中下标为0的元素的引用。*(a+1)即是数组a中下标为1的元素的引用。*(a+i)即数组a中下标为i的元素的引用。而a[i]是上面写法的简记。因为a+i和i+a的含义一样,所以a[i]和i[a]是同一种含义!

对于二维数组 int a[10][13],a则是“数组的数组”。声明指向a的指针的话需要这种形式:int (*p)[13]; 但对于二维数组最好还是使用下标的形式!

3.2非数组的指针

在C语言中,字符串常量代表了一块包括字符串中所有字符以及一个空字符(‘\ 0’)的内存区域。因为C语言要求字符串常量以空字符作为结束标志。

strlen函数返回的字符个数并未包括空字符,因此在用malloc和strlen结合分配内存时需要加上一个空字符。类似这种状况:

malloc(strlen(s)+1);

3.3作为参数的数组声明

将数组作为函数参数毫无意义,C语言会自动地将作为参数的数组声明转换为相应的指针声明。

int strlen(char s[]){ } int strlen(char *s){ }写法相同!

int main(int argc,char **argv){}int main(int argc,*argv[])等价,但前者强调argv是一个指向某数组的起始元素的指针,该数组的元素为字符指针类型。

3.4避免“举隅法”

举隅法(synecdoche)以含义更宽泛的词语来代替含义相对较窄的词语,或者相反。

名词就解释完了,这里只要记住一条:复制指针并不同时复制指针所指向的数据!

3.5空指针并非空字符串

//这节好像没什么东西好记!

#define NULL 0

3.6边界计算与不对称边界

关于边界问题也是我经常犯糊涂的问题,今天看了有些收获!

用第一个入界点和第一个出界点来表示一个数值范围。下界是入界点,包括在取值范围之中;上界是出界点,不包括在取值范围之中。这叫不对称边界:

1.取值范围的大小就是上界与下界之差。

2.如果取值范围为空,那么上界等于下界。这是第一条的直接推论。

3.取值范围为空,上界也永远不可能小于下界。

对于问题:整数x满足边界条件x大于等于16且x小于等于37,那么此范围内x的可能取值的个数是多少?//此博客中好像不能出现小于号,因此用文字代替。

下界是16,包含中!上界是38,38是不包含在其中的。因此用上界减去下界38-16=22 因此可得出答案是22。方便快捷!

对于数组buffer[N],&buffer[N]是一个地址,C语言中引用这个无效元素的地址是允许的,可利用于本例中的不对称边界原则,但引用该元素则是非法的! //这个不对称边界原则在STL中也有应用!

3.7求值顺序

C语言中只有四个运算符(&& || ?: ,)存在规定的求值顺序。而其它运算符对其操作数求值的顺序是未定义的,特别地,赋值运算符并不保证任何求值顺序。因此,在编程中不要对求值顺序作假设!

3.8运算符&&,|| 和 !

3.9整数溢出

注意有符号数的计算!

3.10为main函数提供返回值

这个不用说了,gcc已经明确要求main要返回值!

Comments