《C陷阱与缺陷》第三章

《C陷阱与缺陷》第三章
3.1,指针与数组
数组的一些基本操作都可以通过指针的操作来实现。
实际上C语言为了方便,数组就是利用指针来实现的。

int main{
	int num[5][5];
	int *p;
	int i;
	p=num[4];
	i=num[4][3]; //等价于*(num[4]+3),等价于*(*(num+4)+3)
	return 0;
}

数组和指针最大的区别就是:
数组访问时只需要在符号表中找到相应的地址,然后进行地址的加减即可获得目标数
指针则是通过符号在符号表中找到相应的地址后,凭借这个地址空间中的地址再找到数组目标元素的位置
即指针比数组要多一次地址访问。

int (*monthp)[31]   //monthp表示一个拥有31个整型元素的数组

3.2,非数组的指针
本节主要讲述了char[]和char的区别
主要有:char[]自己分配和销毁空间而char需要自己分配空间和销毁
3.3,作为参数的数组声明
从本节可以看出char[] 和 char*当做函数参数传入并没有太大区别,大部分情况完全相同
但是
extern char *hello 和 extern char[] 有很大的不同,主要原因还是数组和指针取值的区别,在之后的章节中详细介绍。
3.4,避免“举隅法”
本节主要讲述了不要用整体代替部分,考虑问题要全面。
主要一个例子

char *p,*q;
p="xyz";
q=p

p和q实际上是指向同一块内存,即通过哪一个改变都会导致两个都改变。这只是一种改错的思想、
3.5,空指针并非空字符串
当指针变量是0或NULL时不能企图用该指针所指向的内存中存储的内容

if(p==(char *)0) //合法 只是单纯的指针比较
if(strcmp(p,(char*)0)==0) //非法,因为strcmp会获取指针中的内容

在空指针的内部实现中空指针一般是指向0地址即使用全0来表示,也有一些特殊的系统用特定的值来代表空指针。
NULL在stdio.h中被定义为#define NULL ((void *)0)
如果一个指针没有定义初值,则在定义时就随机赋了一个地址,也就是常说的野指针。

注意,void 指针与空指针 NULL 不同:NULL 说明指针不指向任何数据,是“空的”;而 void 指针实实在在地指向一块内存,只是不知道这块内存中是什么类型的数据。

3.6,边界与不对称边界
在编程中,一般有关边界的问题都会有些含糊不清,在编程时很容易就会混乱(我也深受其害)。
在本书中给我们提供了两大原则
(1),首先考虑最简单情况下的特例,然后将得到的结果外推。
(2),仔细计算边界,绝不掉以轻心。
比如要遍历1~10
根据原则一代码必须写成

int i;
for(i=1;i<11;i++)

这就是所谓的不对称边界。即用一个入界点和一个入界点来表示范围。(1为入界点,11为出界点)
在处理一些其他范围(缓存区等)的时候,合理使用此原则也是可以大大降低出错的概率
3.7,求值顺序
此求值顺序和运算符优先级没有关系,求值顺序是指特定的运算符对不同部分的执行顺序。C语言中只有四个运算符有求值顺序(&&,||,?:和逗号)下面一个一个归纳。
&&运算符如果前一个条件为假,则之后的所有条件都不会给予执行。
||运算符如果前一个为真则之后的所有条件都不会给予执行。
?:运算符a?b:c先求值a在根据a判断选取b或c。
逗号运算符先对左侧求值,然后丢弃,再对右侧求值。
下面给出一个对求值顺序做出假设容易出现的错误
对于:y[i]=x[i++];由于求y[i]的地址和i++执行顺序不确定,所以导致程序不安全
3.8,运算符&&,||和!
对于&&和&,||和|运算符,只有两边条件结果都为0或1时才会有可能相同。但是还有有一点区别,&&和||具有求值顺序,&和|没有。
3.9,整数溢出
当两个操作数都为有符号数时,溢出才有可能发生。
最好的办法就是将两个有符号数转换成有符号数
3.10,为函数main提供返回值
当main函数没有定义返回值时,程序会默认返回int类型的数,在定义函数时一定要给函数相应的返回值,不然会返回一个垃圾整数,造成不可预测的后果。


猜你喜欢

转载自blog.csdn.net/hhouxiang/article/details/80653653