我们平常学习的时候用一个指针指向数组名,然后指针就可以和数组也一样通过下标访问了,以至于我们似乎觉得指针和数组没有什么差别。例:
int a[10] = {0};
int * p = a;
p[3] = 6; //ok 没有问题 不会报错
但是我们再看一个例子:
在A文件中:
int a[10];
int *b = a;
在B文件中:
extern int *a;
extern int b[];
.....
int x,y;
x = a[3];
y = b[3];
如果我们将两个文件经过编译汇编后链接到一块后,运行时会报错,大家知道为什么吗,如果大家已经想到了出错原因,想必也已经知道数组名和指针的区别了。
下面我们来分析一下:
在A文件中编译的时候,我们的a是一个数组名,大家看到数组名一定要切记一句话:
数组名就是第一个元素的地址,数组名就是地址。
也许有心的同学会发现当我们对数组名求sizeof的时候它是整个数组所占元素的大小,我们输出
cout<<a<<&a<<endl;
是同一个地址,也就是说a这个数组名本身就代表一个是数组的地址,和我们指针是有区别的,我们定义一个指针p =a;
p这个变量里存放的是a的地址,即数组的首元素地址,但是对p取址是p自己这个变量所占的内存地址,话说了这么多,不如看个图,简洁明了。
我们看看这个图大家应该就明白我们两个赋值语句错在哪里了。
x = a[3];
在我们的B文件中,a是一个指针,也就是说是p这样的结构,它的符号表里应该记录了a本身的地址,而因为是外部符号,所以我们链接的时候会将A文件中定义的数组名即地址0x200赋值给p作为它本身的变量地址,但是指针和数组名处理方式不同,当把它当成一个指针处理的时候,它先找到0x200这个这个地址,认为这是它自己的地址,再从它的空间里面取出值,认为这个内容才是它保存的指向的地址的值,而0x200里的值,想必大家也看出来了,就是a[0],所以它把a[0] 作为指向的地址,把这个值作为一个地址+3*4 = 12 个字节,再取里面的值,这简直就是胡来,这个地址要么是任意地址,要么一不小心就定位到了不可访问的地址,所以运行时会有错误,它发现你非法访问了。我们还是看图再参照上面文字理解一下:
y = b[3];错误的原因也是一样的道理
本来在B文件中定义了一个int b[] 的数组名,但是链接在汇编的时候就已经确定外部符号b的变量地址就作为数组的起始地址来用,还是看图
还是切记一句话:数组名就是首元素地址!!!!!对数组名取址也还是首元素的地址,但是指针就不一样,指针里存储的是数组的首元素地址,但是对指针取址却是它自己的地址,和数组首元素地址不同!!!