C是否为未在函数开始时定义的变量分配内存?

时间:2018-10-05 03:36:20

标签: c memory callstack

这是我提出这个问题的原因:

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,“堆栈”和“ p”是否会出现在调用堆栈中?这不是一个很好的例子,我只想解释这种情况。只需关注问题即可。

2 个答案:

答案 0 :(得分:2)

(为便于说明,我将在问题的原始版本中使用稍微改动的代码。您已经编辑了问题以更改代码,但这并没有改变答案。)

实际上,答案取决于所使用的编译器和优化选项。一些可能的结果:

  1. val完全优化,而与p的值无关。
  2. val放在寄存器中(这算作“内存”吗?)
  3. val放置在堆栈上,而与p的值无关。
  4. 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    $16, %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    $16, %rsp   ## <<< Deallocate val
    popq    %rbp
LBB0_2:
    retq

答案 1 :(得分:0)

尽管编译器有很多回旋余地,尤其是对于经证明不会产生任何可观察到的效果的代码,但C标准确实指定了自动变量(局部变量)的生存期始于该块的输入。声明的内容(这是您要查询的情况下foo的正文。)[注1]

在执行foo之前达到声明为止(如果已达到声明),该变量具有不确定的值,这限制了它的使用。同样,变量的名称在声明之前不可见。但是,变量确实存在(除非已被消除,因为编译器已确定它不相关)。

总的来说,这不值得担心。 “分配”一个自动变量通常包括递减函数入口上的堆栈指针。通过对所有函数的自动变量的大小求和,只需完成一次。电脑不仅仅指望手指,还可以指望电脑。它们可以在与从堆栈指针中减去一个大数相同的时间内减去一个大数。您唯一可能会注意到这种效果的情况是您的函数具有兆字节的局部变量。您应该避免这样做。


注意:

  1. 可变长度数组是该规则的一个例外,因为在知道它们的大小之前不能分配它们,在实际计算该声明之前不能确定它们的大小。