【问题标题】:Does C allocate memory for variables not define on the start of function?C 是否为函数开始时未定义的变量分配内存?
【发布时间】:2019-03-10 12:18:25
【问题描述】:

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

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”是否会在调用堆栈上?这不是一个很好的例子,我只是想解释一下这个场景。只关注问题。

【问题讨论】:

  • 那个例子太简单了。任何好的编译器都会把它优化到一无所有,不管 p。
  • 依赖编译器
  • 如果p为空,那么程序就不会访问val,所以编译器把它放在哪里都没有关系。就您的程序而言,它可以登上月球。 (这被称为“假设”规则。)
  • @usrbin:更新后的示例使答案(如下面的我的示例)似乎与问题脱节,但并没有真正改变 val 可以完全优化掉的事实(新添加的代码等效到return 1)。
  • 您不仅完全更改了问题中的代码,而且将其更改为 C++,尽管问题标记为 C。请不要这样做。 SO 不是对话;它是旨在长期使用的问题和答案的存储库。可以修改问题以使其更精确,但不能是不同的问题。如果您更改问题,现有答案将断开连接,这对响应者不公平,对以后的读者无用。

标签: c memory callstack


【解决方案1】:

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

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

  1. val 被完全优化掉,与 p 的值无关。
  2. val 被放置在一个寄存器中(这算作“内存”吗?)
  3. 无论p 的值如何,val 都被放置在堆栈中。
  4. 仅当 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    $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

【讨论】:

    【解决方案2】:

    尽管编译器有很多余地,特别是对于可以证明不会产生任何可观察效果的代码,但 C 标准确实规定自动变量(局部变量)的生命周期从进入块的入口开始它是声明的(在您询问的情况下,这是foo 的主体。)[注1]

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

    总的来说,这不值得担心。 “分配”一个自动变量通常包括在函数入口处递减堆栈指针;这只需将所有函数的自动变量的大小相加即可完成。计算机不仅限于数手指;他们可以在与减去一个小数相同的时间内从堆栈指针中减去一个大数。您可能会注意到这种效果的唯一一次是您的函数具有兆字节的局部变量。你应该避免这样做。


    注意事项:

    1. 可变长度数组是此规则的一个例外,因为在知道其大小之前无法分配它们,而在实际评估声明之前无法确定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-24
      • 1970-01-01
      • 2015-04-05
      • 2017-01-19
      • 1970-01-01
      • 1970-01-01
      • 2014-04-19
      相关资源
      最近更新 更多