我在for循环中声明了一个数组,并尝试打印其基地址。
#include<stdio.h>
int main(){
int n=16;
for(int i=1;i<=n;i++){
int a[i];
int b[16];
int c[n];
printf("%p %p %p\n",(void *)a,(void *)b,(void *)c);
}
return 0;
}
输出如下:
0x7fffe6191740 0x7fffe6191770 0x7fffe6191700
0x7fffe6191740 0x7fffe6191770 0x7fffe6191700
0x7fffe6191740 0x7fffe6191770 0x7fffe6191700
0x7fffe6191740 0x7fffe6191770 0x7fffe6191700
0x7fffe6191730 0x7fffe6191770 0x7fffe61916f0
0x7fffe6191730 0x7fffe6191770 0x7fffe61916f0
0x7fffe6191730 0x7fffe6191770 0x7fffe61916f0
0x7fffe6191730 0x7fffe6191770 0x7fffe61916f0
0x7fffe6191720 0x7fffe6191770 0x7fffe61916e0
0x7fffe6191720 0x7fffe6191770 0x7fffe61916e0
0x7fffe6191720 0x7fffe6191770 0x7fffe61916e0
0x7fffe6191720 0x7fffe6191770 0x7fffe61916e0
0x7fffe6191710 0x7fffe6191770 0x7fffe61916d0
0x7fffe6191710 0x7fffe6191770 0x7fffe61916d0
0x7fffe6191710 0x7fffe6191770 0x7fffe61916d0
0x7fffe6191710 0x7fffe6191770 0x7fffe61916d0
为什么数组的基地址每次都会更改?是否为每次迭代分配内存。如果是这样,为什么地址在4次迭代中都没有变化?
请说明a
,b
和c
在声明,内存分配和基地址方面的区别。
答案 0 :(得分:6)
这些数组具有自动存储时间,从概念上讲,每次执行{ … }
循环内的for
语句时,都会为每个数组创建一个新实例。由于在各种迭代中,您为数组a
请求了不同的大小,因此C实现将其放置在内存中的不同位置以为其元素留出空间是完全合理的。您的C实现似乎以16个字节的块为单位,以表示它为数组保留的内存量或对齐方式。这可能是堆栈管理的结果,因为数组a
本身可能不需要对齐或块大小。
很有可能a
,b
和c
的分配受到以下事实的影响:在C标准指定的抽象计算机中,{{1}的生存期}一开始执行该块,但是b
和a
的生命周期在执行(“控制”)到达定义它们的语句时开始。这是因为C 2018 6.2.4规定了具有自动存储持续时间且长度不可变的对象在进入关联的块时开始生效(第6段),而具有可变长度的此类对象在声明时具有开始寿命(第7段)。因此,在编写代码时,c
首先开始生命,然后是b
,然后是a
。
此分配顺序影响放置c
的位置,而不影响放置c
的位置。由于b
是首先创建的,因此它在堆栈上“更早”(位于较高的地址,这意味着它获得的地址尚未受b
影响)。由于a
是在以后创建的,因此它在堆栈上“较晚”(在较低的地址处,这意味着其地址受c
的大小影响)。 C标准在技术上并不需要此顺序,因为C实现可以根据需要安排位置,只要获得与C标准定义的结果相同的结果即可。但是,您的实现似乎忠实地遵循了C的抽象计算机模型,先创建a
,然后创建b
,然后创建a
。
另外,打印对象地址的正确方法是使用c
格式规范并将地址转换为%p
:
void *
答案 1 :(得分:4)
在循环的每次迭代中b
的大小都相同。编译器将它定位一次,然后将其放置。
a
和c
都是技术上可变长度的数组。 c
的大小没有变化,但似乎分配给的地址比a
低。
您的数组a
增长了,因此要使该数组远离数组b
,起始地址必须在堆栈上较低。并且,由于c
位于堆栈上a
的下方,因此随着a
的增长,它也随之移动。编译器似乎以16个字节为单位分配堆栈。当数组遇到其他变量时,数组的起始位置又向下移动了16个字节。
一个聪明的编译器可能会发现c
在循环期间是固定大小,然后对其重新排序,以便它出现在堆栈中的a
上方。然后只有a
会更改地址。您的编译器似乎没有这样做。
堆栈布局似乎是:
前4个周期:
b 0x…901d0
a 0x…901a0 gap to b is 0x30
c 0x…90160 gap to a is 0x40
第2个4个周期:
b 0x…901d0
a 0x…90190 gap to b is 0x40
c 0x…90150 gap to a is 0x40
第3个4个周期:
b 0x…901d0
a 0x…90180 gap to b is 0x50
c 0x…90140 gap to a is 0x40
此行为完全由编译器决定。对于a
有1..4条目时b
和a
之间的差距为何如此之大,我没有一个很好的解释。它可以每次将变量放置在不同的位置。从技术上讲,当循环结束时,数组超出范围。在每个循环周期中重新定义变量。它们均未初始化。其中两个无法初始化。您不能为VLA提供初始化程序。
答案 2 :(得分:0)
自动变量分配在堆栈内存中,并且仅存在于所使用的块中。正如@Clonk所述,它与实现有关,因此在不同的实现中会产生不同的结果,但是由于数组的大小,它们是有效的事先不知道,它们最终将出现在连续的内存块中。