理论上没有指定,但实际上不会为单个基本类型对象(如 int、double、T* 等)进行分配。在某些情况下,堆栈指针不会被修改,编译器只是重用其他变量的空间:
for (int i=0 ; i < 10 ; i++)
foo(i);
for (int j=0 ; j < 10 ; j++)
foo(j);
最有可能的是,编译器将重用i 的空间来分配j。这在godbolt.org with gcc 上很容易验证:
.L2:
mov edi, ebx
add ebx, 1
call foo(int)
cmp ebx, 10
jne .L2
xor ebx, ebx
.L3:
mov edi, ebx
add ebx, 1
call foo(int)
cmp ebx, 10
jne .L3
不仅循环是相同的,它们甚至不使用堆栈。而不是堆栈,i 和 j 变量都分配在 `ebx.在这个例子中,分配和释放是完全透明的,只是简单地使用或不使用寄存器。
一个更复杂的示例将在堆栈上执行相同的操作:
int foo(int);
void bar(int*);
void bar()
{
{
int a[10];
for (int i=0 ; i < 10 ; i++)
a[i] = foo(i);
bar(a);
}
{
int b[10];
for (int j=0 ; j < 10 ; j++)
b[j] = foo(j);
bar(b);
}
}
另外,咨询second godbolt.org example 产生:
xor ebx, ebx ; <--- this is simply part of the next block
sub rsp, 48; <--- allocating the stack space
.L2:
mov edi, ebx
call foo(int)
mov DWORD PTR [rsp+rbx*4], eax
add rbx, 1
cmp rbx, 10
jne .L2
mov rdi, rsp
xor ebx, ebx ; <--- this is simply part of the next block
call bar(int*)
.L3:
mov edi, ebx
call foo(int)
mov DWORD PTR [rsp+rbx*4], eax
add rbx, 1
cmp rbx, 10
jne .L3
mov rdi, rsp
call bar(int*)
add rsp, 48 ; <-- deallocating the stack space
同样,这两种情况的代码是相同的。堆栈上没有释放或分配变量。行:
a[i] = foo(i);
被翻译成
mov DWORD PTR [rsp+rbx*4], eax
它只是写相对于堆栈指针(rsp)的数据。它基本上是根据其相对于堆栈指针的位置找到a 的内容。两个代码块之间的堆栈指针没有更新,它只是通过将堆栈指针复制到rdi来传递给bar():
mov rdi, rsp
call bar(int*)
正如我所展示的,通常在块运行期间不会发生分配和解除分配。通常,在函数的开始和结束时,堆栈指针会更新以反映变量。
与简单的整数值不同,具有析构函数的类型更复杂,但我不会对此进行深入讨论,因为问题中没有提出。