C是否为函数开始时未定义的变量分配内存?
Does C allocate memory for variables not define on the start of function?
这是我提出这个问题的原因:
static ProfileUnit* g_units_header;
static ProfileUnit* g_units_tail;
static int g_units_count;
void Destroy() {
if (!g_units_header) {
return;
}
typedef std::vector<ProfileUnit*> PUVect;
PUVect stack(g_units_count);
ProfileUnit* p = g_units_header;
while (p) {
stack.push_back(p);
p = p->next;
}
for (PUVect::const_iterator it = stack.begin(); it != stack.end(); ++it) {
free(*it);
}
g_units_header = g_units_tail = nullptr;
g_units_count = 0;
}
如果 "g_units_header" 是 nullptr,"stack" 和 "p" 会在调用堆栈上吗?这不是一个很好的例子,我只是想解释一下这个场景。只关注问题。
(为了说明,我将在您的问题的原始版本中使用代码的细微变化。您已经编辑了问题以更改代码,但这并没有改变答案.)
实际上,答案取决于所使用的编译器和优化选项。一些可能的结果:
val
被完全优化,而不管 p
. 的值如何
val
放在寄存器中(算作 "memory" 吗?)
val
无论p
. 的值如何,都会被放入栈中
只有当 p
不是 NULL
. 时,val
才会被放入堆栈
在我的机器上,gcc -O3
执行 #4:
$ cat test.c
#include <stdio.h>
void foo(int* p) {
if (!p) return;
int val = 0xdeadbeef;
scanf("%d", &val);
}
编译:
$ gcc -S -O3 test.c
输出(为简洁起见进行了编辑):
$ cat test.s
_foo: ## @foo
testq %rdi, %rdi ## <<< p == NULL?
je LBB0_2 ## <<< Will jump over the stack allocation below
pushq %rbp
movq %rsp, %rbp
subq , %rsp ## <<< Allocate stack for val
movl $-559038737, -4(%rbp) ## 0xdeadbeef
leaq L_.str(%rip), %rdi
leaq -4(%rbp), %rsi
xorl %eax, %eax
callq _scanf
addq , %rsp ## <<< Deallocate val
popq %rbp
LBB0_2:
retq
虽然编译器有很大的回旋余地,特别是对于可以证明不会产生任何可观察到的效果的代码,C 标准确实指定自动变量(局部变量)的生命周期从入口处开始声明它的块(在您询问的情况下是 foo
的主体。)[注 1]
在 foo
的执行期间到达声明之前——如果到达——该变量具有不确定的值,这限制了它的使用。此外,变量的名称在声明之前是不可见的。但是,该变量确实存在(除非它已被删除,因为编译器已确定它无关紧要)。
总的来说,这是不值得担心的。 “分配”一个自动变量通常包括在函数入口处递减堆栈指针;这只需将所有函数的自动变量的大小相加即可完成一次。计算机不仅限于用手指计数;他们可以在与减去一个小数所需的时间相同的时间内从堆栈指针中减去一个大数。您可能会注意到这种影响的唯一情况是您的函数有数兆字节的局部变量。你应该避免这样做。
备注:
- 可变长度数组是此规则的一个例外,因为在已知其大小之前无法分配它们,而在实际评估声明之前无法确定大小。
这是我提出这个问题的原因:
static ProfileUnit* g_units_header;
static ProfileUnit* g_units_tail;
static int g_units_count;
void Destroy() {
if (!g_units_header) {
return;
}
typedef std::vector<ProfileUnit*> PUVect;
PUVect stack(g_units_count);
ProfileUnit* p = g_units_header;
while (p) {
stack.push_back(p);
p = p->next;
}
for (PUVect::const_iterator it = stack.begin(); it != stack.end(); ++it) {
free(*it);
}
g_units_header = g_units_tail = nullptr;
g_units_count = 0;
}
如果 "g_units_header" 是 nullptr,"stack" 和 "p" 会在调用堆栈上吗?这不是一个很好的例子,我只是想解释一下这个场景。只关注问题。
(为了说明,我将在您的问题的原始版本中使用代码的细微变化。您已经编辑了问题以更改代码,但这并没有改变答案.)
实际上,答案取决于所使用的编译器和优化选项。一些可能的结果:
val
被完全优化,而不管p
. 的值如何
val
放在寄存器中(算作 "memory" 吗?)val
无论p
. 的值如何,都会被放入栈中
只有当 val
才会被放入堆栈
p
不是 NULL
. 时,在我的机器上,gcc -O3
执行 #4:
$ cat test.c
#include <stdio.h>
void foo(int* p) {
if (!p) return;
int val = 0xdeadbeef;
scanf("%d", &val);
}
编译:
$ gcc -S -O3 test.c
输出(为简洁起见进行了编辑):
$ cat test.s
_foo: ## @foo
testq %rdi, %rdi ## <<< p == NULL?
je LBB0_2 ## <<< Will jump over the stack allocation below
pushq %rbp
movq %rsp, %rbp
subq , %rsp ## <<< Allocate stack for val
movl $-559038737, -4(%rbp) ## 0xdeadbeef
leaq L_.str(%rip), %rdi
leaq -4(%rbp), %rsi
xorl %eax, %eax
callq _scanf
addq , %rsp ## <<< Deallocate val
popq %rbp
LBB0_2:
retq
虽然编译器有很大的回旋余地,特别是对于可以证明不会产生任何可观察到的效果的代码,C 标准确实指定自动变量(局部变量)的生命周期从入口处开始声明它的块(在您询问的情况下是 foo
的主体。)[注 1]
在 foo
的执行期间到达声明之前——如果到达——该变量具有不确定的值,这限制了它的使用。此外,变量的名称在声明之前是不可见的。但是,该变量确实存在(除非它已被删除,因为编译器已确定它无关紧要)。
总的来说,这是不值得担心的。 “分配”一个自动变量通常包括在函数入口处递减堆栈指针;这只需将所有函数的自动变量的大小相加即可完成一次。计算机不仅限于用手指计数;他们可以在与减去一个小数所需的时间相同的时间内从堆栈指针中减去一个大数。您可能会注意到这种影响的唯一情况是您的函数有数兆字节的局部变量。你应该避免这样做。
备注:
- 可变长度数组是此规则的一个例外,因为在已知其大小之前无法分配它们,而在实际评估声明之前无法确定大小。