在论坛上看到类似下面的一段代码:
a.c:
int a[10]={1,2,3,4,5,6,7,8,9,0};
int *b = a;
b.c:
extern int *a;
extern int b[];
void main()
{
int x, y;
x = a[3];
y = b[3];
}
编译后运行,在main函数中,x和y分别等于什么呢?
用gdb对程序进行调试:
Breakpoint 1, main () at b.c:7
7 x = a[3];
(gdb) p a
$1 = (int *) 0x1
(gdb) p b
$2 = 0x804a068
(gdb) p *b
$3 = 134520896
(gdb) p **b
$4 = 1
可以看到,a的值是1,那么a[3]将是一个越界访问;**b的值才是1,那么b[3]应该还是个越界访问。
数组名被申明成指针、指针被申明成数组名,为什么会产生这样的结果呢?指针与数组名难道不能转换吗?
将数组名赋值给指针,这种转换是由编译器来完成的,编译器会生成一条“将数组名这个不变量的值赋给指针”的指令。然而,b.c在编译过程中,申明的指针a和数组名b是未决的符号(需要通过链接来解决)。所以,这里的类型转换不是编译器能干的事情。然后,在接下来的链接过程中,链接器也不干转换的事情,因为它只关心符号,不关心类型。于是:
在a.c中:
a被定义为数组名,a是一个不变量,没有自己的存储空间,这个不变量的值为134520896(见gdb的打印)。
b被定义为指针,b是一个变量,有自己的存储空间,这个存储空间里面存放的值是a的值,即134520896。
a.c与b.c编译生成的中间文件进行链接后,在b.c中(b.c生成的二进制文件中):
a被申明为指针,a是一个变量,有自己的存储空间,它的存储空间的地址是从a.c中链接过来的,是a.c中的a的值,即134520896。既然a的存储空间的地址是134520896,那么a的值自然就是1了。
b被申明为数组名,b是一个不变量,没有自己的存储空间,它的值是从a.c中链接过来的,是a.c中的b的存储空间的地址,即0x804a068。
可见,在链接过程中,因为数组名本身没有存储空间,所以链接时导出的是数组名所指代的数组的存储空间的地址;而指针本身是有存储空间的,所以链接时导出的是指针本身的地址。如果在这一过程中,数组名和指针发生转换,则会是错位的。