在每次循环迭代中,一次又一次地声明变量j 。那为什么地址保持不变?
- 每次不应该给它一些随机地址吗?
- 此编译器是否依赖?
#include<stdio.h>
#include<malloc.h>
int main()
{
int i=3;
while (i--)
{
int j;
printf("%p\n", &j);
}
return 0;
}
Testrun: -
shadyabhi@shadyabhi-desktop:~/c$ gcc test.c
shadyabhi@shadyabhi-desktop:~/c$ ./a.out
0x7fffc0b8e138
0x7fffc0b8e138
0x7fffc0b8e138
shadyabhi@shadyabhi-desktop:~/c$
答案 0 :(得分:12)
它是堆栈中的内存。它不是从堆中分配的。堆栈在该循环中不会改变。
答案 1 :(得分:6)
为什么会有所不同?编译器需要堆栈上的空间来存储一个int,每次循环都有相同的空间可用。
顺便说一句,你根本就没有使用malloc
。 j
保留在堆栈中。
答案 2 :(得分:6)
j
的地址永远不会改变的原因是因为编译器在输入函数时为堆栈上的j
分配内存,而不是j
进入作用域时。< / p>
与往常一样,查看一些汇编代码可能有助于解释这个概念。采取以下功能: -
int foo(void)
{
int i=3;
i++;
{
int j=2;
i=j;
}
return i;
}
gcc将其转换为以下x86汇编代码: -
foo:
pushl %ebp ; save stack base pointer
movl %esp, %ebp ; set base pointer to old top of stack
subl $8, %esp ; allocate memory for local variables
movl $3, -4(%ebp) ; initialize i
leal -4(%ebp), %eax ; move address of i into eax
incl (%eax) ; increment i by 1
movl $2, -8(%ebp) ; initialize j
movl -8(%ebp), %eax ; move j into accumulator
movl %eax, -4(%ebp) ; set i to j
movl -4(%ebp), %eax ; set the value of i as the function return value
leave ; restore stack pointers
ret ; return to caller
让我们来看看这个汇编代码。第一行保存当前堆栈基指针,以便在函数退出时可以恢复,第二行将堆栈的当前顶部设置为此函数的新堆栈基指针。
第三行是为堆栈分配所有局部变量的内存。指令subl $8, %esp
从堆栈指针的当前顶部esp
寄存器中减去8。堆栈在内存中增长,因此这行代码实际上将堆栈上的内存增加了8个字节。我们在这个函数中有两个整数i
和j
,每个整数需要4个字节,因此它分配8个字节。
第4行通过直接写入堆栈上的地址将i
初始化为3。第5行和第6行然后加载并递增i
。第7行通过将值2写入堆栈中为j
分配的内存中来初始化j
。请注意,当j
进入第7行的范围时,汇编代码没有调整堆栈来为它分配内存,这已经在之前得到了处理。
我确信这很明显,但是编译器在函数开始时为所有局部变量分配内存的原因是因为这样做更有效率。每当局部变量进入或超出范围时调整堆栈将导致对堆栈指针进行大量不必要的操作而无法获得。
我相信你可以弄清楚其余的汇编代码自己做了什么,如果不发表评论,我会引导你完成它。
答案 3 :(得分:4)
j和i分配在堆栈上,而不是堆或freestore(分别需要malloc或new)。堆栈将下一个变量放在确定性位置(堆栈顶部),因此它始终具有相同的地址。虽然如果你在优化模式下运行,变量可能永远不会“解除分配”,也就是说,整个程序中的堆栈大小不会改变,因为它只会浪费周期。
答案 4 :(得分:2)
j
在堆栈上分配,因此在该函数的一次调用期间,它将始终具有相同的地址。
如果您从该循环中调用main()
,则“内部”main
的{{1}}将具有不同的地址,因为它在堆栈中会更高。
有关详细信息,请参阅维基百科上的Hardware Stack。
答案 5 :(得分:2)
其实你没有使用malloc
所以问题是什么?
变量是函数的本地变量,并且在编译期间它的空间在堆栈上保留。那么为什么要在每次迭代时重新分配它?只是因为它在循环中被声明了?
答案 6 :(得分:1)
你不是动物园。它的堆栈地址因此总是因为它总是在堆栈的同一位置一次又一次。
答案 7 :(得分:1)
正如你所说的那样在循环中声明了 ,但是它们都超出了范围并且在每次迭代结束时被“销毁”(也就是说它不在范围内而且不存在当测试循环条件时)。因此,重复使用相同的堆栈位置是完全合法的(事实上,如果不是这样,则会出错)。
答案 8 :(得分:0)
提示:您认为这会做什么?
#include<stdio.h>
#include<malloc.h>
int main()
{
int i=3;
while (i--)
{
int j = 42;
printf("%p\n", &j);
}
return 0;
}
答案 9 :(得分:0)
正如其他答案所说的那样,除了堆叠之外,你不会在这里分配任何东西。但即使您按如下方式修改代码,也不一定会更改分配地址。
这取决于使用的libc,malloc通常位于那里,但是一些应用程序(最着名的是firefox)会覆盖它的使用(内存碎片问题等)。
#include<stdio.h>
#include<malloc.h>
int main()
{
int i=3;
while (i--)
{
int *j = (int *) malloc(sizeof(int));
printf("%p\n", j);
free (j);
}
return 0;
}
如果您注释掉免费(j),您会注意到j的地址确实发生了变化。但是根据你的libc,j的地址可能总是在变化。
答案 10 :(得分:-1)
现在,你会得到一系列泄漏的分配,因为j不会被存储用于后续的免费分配。 j不会随意获得随机地址,但可能只是作为序列与先前的j分配相比。
如果你在循环结束时释放j,你可以获得与以前相同的行为,具体取决于malloc和free的实现。
编辑:您可能需要使用此代码重新检查打印值。