数组 != 指针

大多数用户在使用C\C++时都认为数组和指针是等同的,即

char str[] = "I am a string";
char *ptr = str;
char c = str[0];
c = ptr[0];

数组可以赋值给指针,数组和指针都可以使用下标来访问数组元素,所以 数组 = 指针

假设以上推论是成立的,那么请看下面语句:

// test 0, 定义的数组声明为指针
char str[] = "I am a string";
extern char *str;
char c = str[0];

// test 1,定义的指针声明为数组
char *ptr = "I am a string";
extern char ptr[];
char c = ptr[0]

如果 数组 = 指针,那么上述代码应该能够正确运行。

但是!!!上面的代码是错误的,虽然没有语法错误,但在运行时 c 的值是错误的,是不确定的,str[0] 和 ptr[0] 指向了错误的地方。

要想找到错误的原因得从编译器的角度来看待数组和指针。

下面是在vs2015 32 Debug 下关于数组和指针下标取值的汇编代码:

// 数组 str[0]
001A178E  mov         eax,1  
001A1793  imul        ecx,eax,0  
001A1796  mov         al,byte ptr str (01A9000h)[ecx]

// 指针 ptr[0]
0012178E  mov         eax,1  
00121793  imul        ecx,eax,0  
00121796  mov         edx,dword ptr [ptr (0129000h)]  
0012179C  mov         al,byte ptr [edx+ecx]  

在编译器编译过程中,数组的大小是已知的,数组的地址也就是已知的,数组的地址存放在符号表中,所以数组元素的地址即为:

下标(0) * 数组类型大小(sizeof(char)) + 数组首地址(0129000h)    

指针就不同了,它本身是一个变量,4字节大小(32位环境下),存储的是指向物的地址,它的值只有在运行时才能确定,所以在取地址元素时需要多一步操作,即取出它存储的地址值来当做数组的首地址:mov edx,dword ptr [ptr (0129000h)] ,取数组元素的方式就为:

下标(0) * 指针指向类型大小(sizeof(char)) + 指针指向地址(有取址操作,mov edx, [ptr]) 

所以在上面的test0中,将数组声明为指针,当执行 char c = str[0]; 时,会先取指针的值,并以此作为数组的首地址,然后加上偏移量来获取元素值,但是此时的指针原来就是数组,只是声明时声明成了指针,所以在取指针的值时取得的是字符串的值,即 “I_am”(_代表空格,32位环境下指针是4字节大小,所以取前4个字符),由此值再加上偏移值去取元素,显然这是错误,轻则导致程序崩溃,重则导致程序运行错误(不可控的程序是非常严重的,而且有时候会很难发现这种错误,崩溃会立刻提醒你这里有个bug,这反而是相比较较轻的问题)。

同样在test1中,将指针声明为数组,当执行 char c = ptr[0]; 时,会把指针的地址(不是指指针存储的地址,相当于 &ptr )当成数组的首地址,这同样会导致不可预知的错误。

综上所述,指针与数组是不能等同的,但是也不必处处小心翼翼,因噎废食,减少指针的使用,在通常情况下数组和指针的互操作是安全的,只要在声明时注意一定要与定义一致即可。更详细的信息可以查看书籍 《C专家编程》第4章 令人震惊的事实:数组与指针并不相同。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值