现在的位置: 首页 > 综合 > 正文

C陷阱与缺陷书摘 —语义“陷阱”

2013年08月24日 ⁄ 综合 ⁄ 共 1475字 ⁄ 字号 评论关闭

1.
指针和数组

 

C语言中数组值得注意的地方有两点:

1)C语言中只有一维数组,而且数组大小必须在编译器就作为一个常数确定下来。C语言中数组的元素可以是任何类型,包括另外一个数组。所以,可以仿真出一个多维数组。

2)对于一个数组,我们只能做两件事:确定该数组大小,以及获得指向该数组下标为0的元素的指针。其它有关操作,哪怕咋看上去是以数组下标进行运算,实际上都是通过指针进行的。换句话说,任何一个数组下标运算,都等同于一个对应的指针运算。

 

如果在应该出现指针的地方,却采用了数组名来替换,那数组名就被当做指向该数组下标为0的元素的指针。

 

对于某数组名a (譬如 int a[3]中的a),除了被用作运算符sizeof的参数这一情形,其它所有的情形中a都代表指向数组a中下标为0的元素的指针。正如我们的期待,sizeof(a)是整个数组a的大小,而不是指向a中元素的指针的大小。

 

*(a+i)即是数组a中下标为i的元素的引用,常被简写为 a[i];由于a+i和i+a的含义一样,所以,a[i]和i[a]具有同样的含义。

 

对于二维数组,int cal[12][31]; cal是一个有着12个数组类型元素的数组,它的每个数组类型元素又是一个有着31个整形元素的数组。

                                  int *p;

                                  p=cal;

以上关于p的赋值是非法的:cal是二维数组,即“数组的数组”,在此处,使用cal会将cal转换为一个指向数组(cal[0])的指针,而p是一个指向整形变量的指针,“p = cal”试图将一个类型的指针赋值给另一种类型的指针。

 

2. 作为参数的数组声明

 

C语言中,无法将一个数组作为函数参数直接传递。使用数组名作参数时,数组名会立刻被转换为指向该数组第一个元素的指针。因此,将数组作为函数参数毫无意义:c语言中会自动地将作为参数的数组声明装换为相应的指针声明。

 

注:在其它情况下,并没有这样的自动转换。


3. 空指针并非空字符串

 

除常数0外,在c语言中将一个整数转换为一个指针,最后得到的结果取决于编译器实现。对于0,编译器保证由0转换而来的指针不等于任何有效的指针。
出于代码文档化的考虑,常数0常被一个符号来代替: #define NULL 0 。

 

注意:当0被转换为指针使用时,该指针绝对不能被解除引用(dereference)。换句话说,当我们把0赋值给一个指针变量时,绝对不能企图使用该指针所指向的内存中存储的内容。

 

4. 求值顺序

 

C语言中,只有4个运算符(&&、||、?:和,)存在规定的求值顺序。运算符&&和||首先对左侧操作数求值,只有需要时才对右侧操作数求值。运算符?:有三个操作数:在a?b:c中,a首先被求值,根据a的值再求操作数b或c的值。而逗号运算符,首先对左侧操作数求值,然后该值被“丢弃”,再对右侧操作数求值。

 

C语言中其它任何运算符对其操作数求值顺序都是未定义的。特别地,赋值运算符并不保证任何求值顺序。

 

5. 整数溢出

 

C语言中存在两类整数算术运算,有符号运算与无符号运算。在无符号算术运算中,没有“溢出”:所有的无符号运算都是以2的n次方为模,这里的n是结果中的位数。如果算术运算符的一个操作数是有符号整数,另一个是无符号数,则有符号数会被转换为无符号数,也不可能发生“溢出”。但是,当两个操作数都是有符号整数时,就有可能发生“溢出”,而且“溢出”的结果是未定义的。当一个运算的结果发生“溢出”时,做出任何假设都是不安全的。

 

 



 

 

 

 

 

抱歉!评论已关闭.