数组访问算法是在编译时还是运行时完成的?

时间:2012-06-28 09:58:53

标签: c

让我们说我有一个数组

int aVar[10];
...
...
for(i=0; i<10; i++)
    aVar[i] = i*10;

在这里,我所知道的是,数组被引用为指针,索引值的位置使用类似于(base address of aVar) + sizeof(int) * i的方式计算。如果我错了,请纠正我。

我的问题:

这个计算在运行可执行文件之前是否已由编译器完成,或者这个算术计算是在执行时在数组中找到确切的位置?

当然,我们无法在编译时获取aVar的地址。

3 个答案:

答案 0 :(得分:4)

“当然我们在编译时无法获取aVar的地址?”

是的,实际上,我们可以或足够近。如果它是全局的或静态的,那么它将占据链接器可以解析的固定位置 - 不是完全编译时间而是在运行时。如果它在堆栈中,堆栈指针的偏移在编译时是已知的,因此不需要在机器(例如PC)上计算任何地址,其中地址可以采用表格偏移+寄存器的内容。

答案 1 :(得分:2)

名义上它是在运行时完成的,但标准并不在意,只要结果是正确的(标准称之为“as-if”规则)。这取决于编写C实现的人,它可能取决于您使用的优化选项。

如果编译器展开循环,那么它将知道堆栈指针中aVar[0]aVar[1]等的偏移量,就像它知道堆栈指针中aVar的偏移量一样。所以代码看起来没有不可避免的障碍:

store 0 at some constant offset from the stack pointer
store 10 at a slightly larger constant offset from the stack pointer
etc.

答案 2 :(得分:0)

“无法在编译时获取aVar的地址”。是的,但编译器可以生成代码以在进入循环之前计算其地址,并且性能明智地是需要优化的循环体,而不是预循环代码。

一个好的编译器优化它会将该基地址放入循环内的寄存器中,这样它就不必重新获取它,或者注意到你的数组访问以与索引相同的方式扫描数组。一个非常聪明的编译器会意识到i * 10是从数组索引计算出来的,没有别的东西,并且也替换了索引。所以编译的优化代码可能更像是:

int aVar[10];
...
...
register int* p = &aVar;  // my C syntax may be slight wrong here
for(i=0; i10<10*10; i10+=10)
    *p = i10;

(循环也可以反转,因此i10可以从100下降零开始计算。由于减法通常会在广泛使用的机器中生成条件代码,因此可以保存比较指令。)