从编译原理理解数组名

参考文章:
http://blog.chinaunix.net/uid-27004869-id-3301282.html

编译器在做词法分析和语法分析的时候,遇到一个数组的定义,就会把数组的有关信息汇集在一个叫做“内情向量”或“信息向量”的表格中,其中的信息包括数组的类型,维数,各维的上、下边界,以及数组的首地址,然后将这个“内情向量”相关信息存储在符号表中。
数组定义后位置就是固定的,因此其首地址可以在编译阶段得到。

当编译器到达代码生成的各阶段时,每次遇到数组名这个标识符,编译器都会从符号表中取出这个数组的首地址,然后用这个地址来替代数组名,例如,假设数组a起始地址是0x8048000,则a[1]就会被编译器转化成*(0x8048000+1),
因此在生成的汇编代码中,数组名已经完全被转化成一个常量,一个固定的数(地址)。
但是
对于指针p,它是一个变量,其值存储在地址&p中,这个值在编译时是不能得到的。

这里说的这一点是非常关键的,我们通过两种方法来验证这个问题:
1、
int a;
int b[10];
int c;
int d;
printf(“%p %p %p %p”, &a, &b, &c, &d);
打印地址大致如下:
0x167654 0x1675f0 0x167658 0x16765c
可以看出a, c, d地址是连在一起的,相差4个字节 。至于数组究竟在内存中怎么存储,还没有找到合适答案,有一点应该可以肯定,它应该也在栈上,之前遇到一个数组越界访问,踩掉了函数的返回地址
2、汇编
通过g++ -s,汇编语言发现,其实并没有出现一个常量地址,
一段代码:
int func()
{
int f = 3;
int g[2] = {4, 5};
int h= 1;
return f+g[0]+h+sizeof(g)+(long)&g;
}
汇编代码:
movq %rsp, %rbp //r表示64位
movl 3,8(movl 4, -16(%rbp)
movl 5,12(movl 1, -4(%rbp)
movl -16(%rbp), %eax
addl -8(%rbp), %eax
add1 -4(%rbp), %eax
movl %eax, %edx
leaq -16(%rbp), %rax
add1 %edx, %eax
add1 $8, %eax
通过汇编代码我们看到,至少是使用linux g++编译出来的数组名并非一个常量,它只不过这个符号中保存了更详尽的信息

你可能感兴趣的:(C++,C++)