在C语言中,大括号是否充当堆栈框架?
如果我在一组新的大括号内创建一个变量,该变量是从右大括号的堆栈中弹出的,还是一直挂到函数结束?例如:在C语言中,大括号是否充当堆栈框架?,c,memory,stack,C,Memory,Stack,如果我在一组新的大括号内创建一个变量,该变量是从右大括号的堆栈中弹出的,还是一直挂到函数结束?例如: void foo() { int c[100]; { int d[200]; } //code that takes a while return; } d是否会在需要一段时间的代码部分占用内存?否,大括号不充当堆栈框架。在C语言中,大括号只表示一个命名范围,但当控件从堆栈中传递出去时,不会破坏任何内容,也不会弹出任何内容 作为一名编写代码的程序员,
void foo() {
int c[100];
{
int d[200];
}
//code that takes a while
return;
}
d
是否会在需要一段时间的代码部分占用内存?否,大括号不充当堆栈框架。在C语言中,大括号只表示一个命名范围,但当控件从堆栈中传递出去时,不会破坏任何内容,也不会弹出任何内容
作为一名编写代码的程序员,您通常可以将其视为堆栈框架。大括号内声明的标识符只能在大括号内访问,因此从程序员的角度来看,就好像它们在声明时被推到堆栈上,然后在退出作用域时弹出。然而,编译器不必生成在进入/退出时推送/弹出任何内容的代码(通常情况下,它们不需要)
还要注意的是,局部变量可能根本不使用任何堆栈空间:它们可以保存在CPU寄存器或其他一些辅助存储位置,也可以完全优化
因此,理论上,d
数组可以为整个函数消耗内存。但是,编译器可能会对其进行优化,或者与使用寿命不重叠的其他局部变量共享内存。我认为它确实超出了范围,但在函数返回之前不会弹出堆栈。因此,在函数完成之前,它仍将占用堆栈上的内存,但在第一个右大括号的下游无法访问。它取决于实现。我编写了一个简短的程序来测试GCC4.3.4的功能,它在函数开始时一次分配所有堆栈空间。您可以使用-S标志检查gcc生成的程序集。否,在例程的其余部分,d[]将不在堆栈上。但是alloca()是不同的
编辑:克里斯托弗·约翰逊(以及西蒙和丹尼尔)是对的,我最初的反应是错了。对于CYGWIN上的gcc 4.3.4.1,代码:
void foo(int[]);
void bar(void);
void foobar(int);
void foobar(int flag) {
if (flag) {
int big[100000000];
foo(big);
}
bar();
}
给出:
_foobar:
pushl %ebp
movl %esp, %ebp
movl $400000008, %eax
call __alloca
cmpl $0, 8(%ebp)
je L2
leal -400000000(%ebp), %eax
movl %eax, (%esp)
call _foo
L2:
call _bar
leave
ret
活到老学到老!快速测试似乎表明AndreyT在多次分配方面也是正确的
添加得太晚了:上面的测试表明该方法不太正确。多年来,它一直在说(重点补充):
“当数组名的作用域结束时,变长数组的空间即被释放。”
你的问题不够清楚,无法明确回答
一方面,编译器通常不会对嵌套块作用域执行任何本地内存分配解除分配。本地内存通常只在函数进入时分配一次,在函数退出时释放
另一方面,当本地对象的生存期结束时,该对象占用的内存可以在以后重新用于另一个本地对象。例如,在这段代码中
void foo()
{
{
int d[100];
}
{
double e[20];
}
}
两个数组通常占用相同的内存区域,这意味着函数foo
所需的本地存储总量是两个数组中最大的一个所需的,而不是同时对两个数组所需的
后者是否符合d
继续占用内存直到问题上下文中的函数结束由您决定。您的变量d
通常不会从堆栈中弹出。大括号不表示堆栈框架。否则,您将无法执行以下操作:
char var = getch();
{
char next_var = var + 1;
use_variable(next_char);
}
如果大括号导致真正的堆栈推送/弹出(就像函数调用一样),那么上面的代码将不会编译,因为大括号内的代码将无法访问大括号外的变量var
(就像子函数无法直接访问调用函数中的变量一样)。我们知道情况并非如此
大括号只是用于范围界定。编译器将把从括号外对“内部”变量的任何访问视为无效,并可能将该内存重新用于其他用途(这取决于实现)。但是,在封闭函数返回之前,它可能不会从堆栈中弹出
更新:以下是。关于具有自动存储持续时间的对象(第6.4.2节):
对于没有可变长度数组类型的对象,其
生存期从与之关联的块的入口开始延长
直到该块的执行以任何方式结束
同一节将术语“寿命”定义为(重点):
对象的生存期是程序执行过程中的一部分
保证为其保留的存储。存在一个对象,
具有恒定地址,并始终保留其最后存储的值
它的一生。如果对象在其生存期之外被引用,则
行为是未定义的
当然,这里的关键词是“保证”。一旦离开内部大括号集的范围,数组的生存期就结束了。存储可能会分配给它,也可能不会分配给它(编译器可能会将空间重新用于其他用途),但任何访问数组的尝试都会调用未定义的行为,并导致不可预知的结果
C规范没有堆栈框架的概念。它只说明结果程序将如何运行,并将实现细节留给编译器(毕竟,在无堆栈CPU上的实现与在有硬件堆栈的CPU上的实现会有很大的不同)。C规范中没有规定堆栈帧将在哪里结束或不在哪里结束。唯一真正知道的方法是在特定的编译器/平台上编译代码并检查生成的程序集。编译器当前的一组优化选项也可能在这方面发挥作用
如果要确保阵列d
不再是ea
void foo() {
int c[100];
int *p;
{
int d[200];
p = d;
}
/* Can I access p[0] here? */
return;
}
#include <stdio.h>
int main() {
int* x;
int* y;
{
int a;
x = &a;
printf("%p\n", (void*) x);
}
{
int b;
y = &b;
printf("%p\n", (void*) y);
}
}
#include <stdio.h>
int main() {
int* x;
int* y;
{
int a;
x = &a;
}
{
int b;
y = &b;
}
printf("%p\n", (void*) x);
printf("%p\n", (void*) y);
}