c缺陷与陷阱 第3章 语义陷阱

1. 指针与数组

c语言中的数组要注意以下两点:

下面的方法存在问题,因为数组的大小必须在编译期作为一个常数确定下来。

a[3][4] 本质也是一个一维数组(包含3个元素),只不过一维数组中的元素是也是数组(包含4个元素)。

int  ar[10] = { 0 }; 注意:数组名代表的是整个数组空间。由sizeof(ar)=40  可以体现。

三者的内容相同,但解释是不同的。

&a[0] 针对首个元素取地址。

ar 数组名包含的内容,恰好是数组首元素的地址。但是数组名和数组首元素的地址不能划为等号。

&ar  数组空间的地址  

&ar[0] 和ar都是普通的整型指针

这里要明白,&ar实际类型是数组指针

二维数组 (实际没有二维数组,只是一维数组的元素还为数组)

对于二维数组而言,数组名同样表示数组首元素的地址,而ar的首元素还是个

数组,因此,对应的指针也应该是数组指针。

二维数组, int ar[3][4];
ar实质上是一个指针的指针, 即二级指针。
一个指针加一个数, 当然还是指针(即a+1是指针)
取值一个二级指针, 是一级指针(即指针), 所以还是指针, 不过类型发生了变化.
a+1的类型为int * [4].
而*(a+1)的类型则为int *, 也就是平常的指针。

因此,为二维 数组中某一元素赋值用*(*(ar+1)+1)= 100.

2. 非数组的指针

r为指针 即地址。

想往r中拷贝 就需要为r开辟空间。

malloc开辟的空间是10,后面的\0没有空间存储。此时不会报错的

原因是恰好第11个位置没有被占用,如果被占用了就会报错。

为了避免上述问题,开辟的空间加1.

char *r = (char *)malloc(strlen(s)+strlen(t)+1)

同理,要想往 char *name中拷贝就需要为name开辟空间

3. 作为参数的数组声明

(1)

a和b中的值均为数组首元素的地址

int a[5] = {1,2,3,4,5};

printf("%p",a); //打印出的是a的地址

由于整型数组没有结束标记,打印时 只能把a当做地址来打印。

char b[ ] = "hello";

printf(b);   //打印出的是字符串 hello

原因:字符数组和字符指针可通过首地址输出整个数组,整形数组却不可以。原因分析如下:
String是一个类,会重载toString()函数,toString()返回其本身。所以直接输出String类的对象会输出具体字符串。而字符型数组在内存中的储存方式同String类一样,因此输出数组名并非输出数组首地址,而是直接输出数组内容直到/0;而整形数组只是整形的集合,所以其数组名仅仅是其首地址,需用for循环输出整个整形数组。

举例:

值分别为40和4

说明数组名和数组首元素地址并不能划为等号,只是数组名的值是首元素地址。

因此不能对数组进行整体赋值。即br = ar不可取。

两个fun函数等价。

在调用函数fun(br)时,表面上看起来像传输的是整个br数组,实际传输的是br的首地址

因此注意,如果函数的参数是以数组的形式传递,那么,数组形式要退化为指针。

4. 避免举偶法陷阱

混淆指针与指针所指向的数据。

*p空间存储的并不是hello字符串

系统会开辟两个空间,一是为hello开辟的静态常量空间  其实位置是0x0042201c

二是为指针变量*p在栈区开辟的空间0x0013ff7c。而空间0x0013ff7c存储的是hello空间的首地址0x0042201c。

陷阱一:以下两种操作,字符串的存储方式不同。

char *p = “hello”;

p[0] =‘H’; 程序会崩溃,原因是不允许修改 静态常量空间中的 字符串常量。

而char p[ ] = "hello";

p[0] = 'H';   则没问题,p[]是数组空间,会在栈区开辟空间,在栈区修改数组的内容是完全可以的。

陷阱二:

char *p = "hello";

char *q = p;

此时,p和q对应的栈区空间地址不同,但是这两个地址中存放的内容是相同的,都是hello的存储空间的首地址,即两个指针指向同一个地址。

陷阱三:

此时,不能重复释放空间。

5. 空指针并非空字符串

char *p = NULL; //p没有任何指向

char *q ="";   //q指向的内容为\0

6. 边界计算与不对称边界

数组 左闭右开  栈的生长方向是从高地址到低地址。

图中,内存上边是低地址 下面是高地址,所以i在高地址 a在低地址

如上程序会一直循环,

原因是:在内存中,a[10]的位置恰好为i,a[10]相当于对i赋值,这会导致i的值一直小于10。

栈:它的生长方式是向下的,是向着内存地址减小的方向增长。栈的开口是向下的,上面的底部是栈底,下面的开口是栈顶。

缓冲区设计

7. 求值顺序陷阱

短路求值

8. 运算符&& ||  !  按位 & | ~(取反)

逻辑运算的结果只能是真或假

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值