变量 j
的地址从未改变的原因是编译器在函数进入时就为 j
在栈上分配了内存,而不是在 j
进入作用域时分配。
像往常一样,查看一些汇编代码可能有助于解释这个概念。看下面的函数:
int foo(void)
{
int i=3;
i++;
{
int j=2;
i=j;
}
return i;
}
gcc将其转换为以下x86汇编代码:
foo:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl $3, -4(%ebp)
leal -4(%ebp), %eax
incl (%eax)
movl $2, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
leave
ret
让我们来看一下这段汇编代码。第一行保存当前的堆栈基指针,以便在函数退出时恢复,第二行将当前堆栈顶设置为此函数的新堆栈基指针。
第三行是为所有本地变量在堆栈上分配内存的行。指令“subl $8, %esp”从当前堆栈指针(esp寄存器)中减去8。由于堆栈向下增长,因此这行代码实际上增加了8字节的堆栈内存。在此函数中有两个整数i和j,每个都需要4个字节,因此它分配了8个字节。
第4行通过直接写入堆栈上的地址将i初始化为3。然后第5行和第6行加载并递增i。第7行通过将值2写入为j分配的内存来初始化j。请注意,当j在第7行进入作用域时,汇编代码没有调整堆栈以为其分配内存,这已经在前面处理过了。
我相信这很明显,但编译器在函数开始时分配所有本地变量的内存的原因是因为这样做更有效率。每次本地变量进出作用域时调整堆栈会导致大量不必要的堆栈指针操作而没有任何收益。
如果你还不确定这段汇编代码的其余部分是做什么的,那没关系,请留言,我会帮你解释。
malloc
,那么每次j
都会被分配不同的内存,只有当你每次不释放它时才会这样。 - N 1.1malloc
时使用隐式转换,因为GCC会进行隐式转换,并且不会抛出任何警告,因为这样做是完全安全的。在C中,不需要强制转换由malloc
返回的指针。请查看以下链接以获取更多信息:http://stackoverflow.com/questions/1942159/need-some-clarification-regarding-casting-in-c/1942276#1942276 https://dev59.com/XUrSa4cB1Zd3GeqPX55J#1835201 https://dev59.com/dHRB5IYBdhLWcg3wgHWr#605858 现在我表达清楚了吗? :) - N 1.1