11

(抱歉英语不好。)

问题 1。

void foo(void)
{
    goto inside;
    for (;;) {
        int stack_var = 42;
inside:
        ...
    }
}

stack_var当我转到inside标签时,会在堆栈中分配一个位置吗?即我可以正确使用stack_var内的变量...吗?

问题2。

void foo(void)
{
    for (;;) {
        int stack_var = 42;
        ...
        goto outside;
    }
outside:
    ...
}

当我转到标签时,会在stack_var释放的堆栈中占有一席之地吗?例如,在内部outside做是否正确?return...

换句话说,goto正确使用堆栈变量(当我遍历块时自动(取消)分配)是聪明的,还是只是一个愚蠢的跳跃?

4

2 回答 2

10

问题一:

我可以在 ... 中正确使用 stack_var 变量吗?

... 中的代码可以写入stack_var. 但是,这个变量是未初始化的,因为执行流程跳过了初始化,所以代码不应该在没有先写入的情况下读取它。

从 C99 标准,6.8:3

每次按执行顺序到达声明时,都会评估具有自动存储持续时间的对象的初始化程序 [...],并将值存储在对象中(包括在没有初始化程序的对象中存储不确定的值)

我的编译器将下面的函数编译为一个程序集,该程序集有时会返回未初始化的内容x

int f(int c){
  if (c) goto L;
  int x = 42;
 L:
  return x;
}

    cmpl    $0, %eax
    jne LBB1_2
    movl    $42, -16(%rbp)
LBB1_2:
    movl    -16(%rbp), %eax
...
    popq    %rbp
    ret

问题2:

当我转到外部标签时,stack_var 会在堆栈中释放一个位置吗?

stack_var是的,您可以预期,一旦变量超出范围,保留的内存就会被回收。

于 2012-11-11T10:05:53.023 回答
2

有两个不同的问题:

  • C代码中变量的词法作用域。C变量仅在声明它的块内才有意义您可以想象编译器正在将变量重命名为唯一名称,这仅在作用域块内有意义。

  • 在生成的代码中调用框架。一个好的优化编译器通常将当前函数的调用帧分配在函数开头的机器类栈上。该调用帧中的给定位置(称为)可以(并且通常)被编译器重用于多个局部变量(或其他目的)。

并且局部变量只能保存在寄存器中(调用帧中没有任何插槽),并且该寄存器显然会被用于各种目的。

对于第一个案例,您可能会伤害未定义的行为。之后goto insidestack_var未初始化的。

我建议您编译gcc -Wall并改进代码,直到没有给出警告。

于 2012-11-11T10:05:53.373 回答