C 再次分配相同的内存空间&;再一次
在每个循环迭代中,变量j被反复声明。那为什么它的地址保持不变呢C 再次分配相同的内存空间&;再一次,c,linux,unix,gcc,C,Linux,Unix,Gcc,在每个循环迭代中,变量j被反复声明。那为什么它的地址保持不变呢 不是每次都应该给它一些随机地址吗 这个编译器依赖吗 它是堆栈上的内存。它不是从堆中分配的。堆栈在该循环中不会更改。您没有进行malloc-ing。它是一个堆栈地址,所以它总是相同的,因为它总是一次又一次地位于堆栈的相同位置。j是在堆栈上分配的,因此在该函数的一次调用期间,它将始终具有相同的地址 如果从该循环中调用main(),则“内部”main的j将具有不同的地址,因为它在堆栈中的位置更高 有关更多详细信息,请参见维基百科。为什
- 不是每次都应该给它一些随机地址吗李>
- 这个编译器依赖吗
它是堆栈上的内存。它不是从堆中分配的。堆栈在该循环中不会更改。您没有进行malloc-ing。它是一个堆栈地址,所以它总是相同的,因为它总是一次又一次地位于堆栈的相同位置。
j
是在堆栈上分配的,因此在该函数的一次调用期间,它将始终具有相同的地址
如果从该循环中调用main()
,则“内部”main
的j
将具有不同的地址,因为它在堆栈中的位置更高
有关更多详细信息,请参见维基百科。为什么会有所不同?编译器需要堆栈上的空间来存储int,并且每次通过循环时,相同的空间都可用
顺便说一下,您实际上根本没有使用
malloc
j
保存在堆栈上。实际上您没有使用malloc
,那么问题出在哪里
该变量是函数的局部变量,在编译期间其空间保留在堆栈上。。那么为什么每次迭代都要重新分配呢?仅仅因为它是在循环中声明的?j和i是在堆栈上分配的,而不是在堆或freestore上分配的(这分别需要malloc或new)。堆栈将下一个变量置于确定性位置(堆栈顶部),因此它始终具有相同的地址。虽然如果您在优化模式下运行,变量可能永远不会“解除分配”,也就是说,堆栈大小在整个程序中不会改变,因为这只是浪费周期。现在,您会得到一系列泄漏的分配,因为j不会存储以供后续免费使用。j不一定会得到随机地址,但与j以前的分配相比,可能只是一个序列 如果在循环结束时释放j,根据malloc和free的实现,可以获得与以前相同的行为
编辑:您可能需要使用此代码重新检查打印的值。提示:您认为这样做有什么作用
#include<stdio.h>
#include<malloc.h>
int main()
{
int i=3;
while (i--)
{
int j = 42;
printf("%p\n", &j);
}
return 0;
}
#包括
#包括
int main()
{
int i=3;
而(我--)
{
int j=42;
printf(“%p\n”,&j);
}
返回0;
}
正如您所说,它是在循环中声明的,但它都超出了范围,并且在每次迭代结束时被“销毁”(即它不在范围内,并且在测试循环条件时不存在)。因此,重复使用相同的堆栈位置是完全合法的(事实上,如果不是这样的话,这将是一个错误)。正如其他答案所说,这里除了在堆栈上进行分配外,没有任何分配。但即使您按如下方式修改代码,也不一定会更改分配地址
这取决于所使用的libc,malloc通常位于那里,但一些应用程序(尤其是firefox)会覆盖它以供使用(内存碎片问题等)
#包括
#包括
int main()
{
int i=3;
而(我--)
{
int*j=(int*)malloc(sizeof(int));
printf(“%p\n”,j);
免费(j);
}
返回0;
}
如果你把免费的(j)注释掉,你会注意到j的地址确实改变了。但是,根据您的libc,j的地址可能总是会更改。之所以
j
的地址从不更改,是因为编译器在输入函数时为堆栈上的j
分配内存,而不是j
进入作用域时
和往常一样,查看一些汇编代码可能有助于解释这个概念。执行以下功能:-
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行进入作用域时,汇编代码没有调整堆栈以为其分配内存,这在前面已经得到了处理
我确信这是显而易见的,但编译器在函数开始时为所有局部变量分配内存的原因是这样做效率更高。每次局部变量进入或超出范围时调整堆栈都会导致对堆栈指针进行大量不必要的操作而没有任何收益
我相信,如果你不发表评论,你可以自己找出汇编代码的其余部分是做什么的,我会带你看一遍。我错发了一个错误的问题。。我的错。。很抱歉我已经更新了question@Gardener:很好<代码>无效*将自动执行
cast
ed。您现在已更改问题。你是否测试过它,看看你最初的断言是否仍然正确?它甚至不是有效的代码,所以答案几乎肯定是否定的。@nvl:Implicit cast from void*to int!?我
#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;
}
int foo(void)
{
int i=3;
i++;
{
int j=2;
i=j;
}
return i;
}
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