c - 在 if block 中声明变量的确切效果

标签 c scope icc

我想了解以下 C 语言的效果:

int func(int arg) {
    if (arg == 0) {
        double *d = malloc(...);
    }
    //...
}

我的理解是:

  • 无论arg 的值如何,调用func 时都会为指针d 腾出栈空间
  • d 仅被初始化,即调用 malloc,如果 arg == 0
  • d 只能在 if block 内访问;尝试在外部访问它会产生编译错误 - 即使无论如何分配了 d 的堆栈空间。

因此,除了阻止访问 if block 之外的范围规则外,它等同于以下内容:

int func(int arg) {
    double *d;
    if (arg == 0) {
        d = malloc(...);
    }
    //...
}

这是正确的吗?我正在使用 icc 默认设置进行编译,这似乎是 std=gnu89

最佳答案

d 表示的对象的生命周期从声明它的 block 的开头开始(可能在声明之前),不一定在函数的开头。实际上,编译器可能会选择在函数入口处为所有变量分配空间;例如,Gcc 将 func 的两个版本编译成相同的程序集。一个函数中只有几个自动变量,很可能它们都放在寄存器中,根本没有堆栈空间用于它们。

初始化发生在初始化器出现的地方。所有这些都遵循 as-if 规则(一如既往):在这种情况下,Gcc 在优化时不会生成任何对 malloc 的调用(从而消除内存泄漏),编译器是允许的“知道”标准库函数的作用。如果这不是一个库函数并且编译器不知道该定义,则该调用保证在到达初始化程序时准确发生。

使用未声明的标识符(或超出范围的标识符)是语法错误,因此会在编译时被捕获。表示对象的生命周期(具有自动存储持续时间)以封闭 block 结束,之后任何引用它的尝试(通过用于指向对象的指针)都是未定义的,不需要诊断。

在第二个代码片段中,不仅在语法上可以在 if block 之后使用 d,它还被定义为访问表示的对象。

为了说明标识符的范围和所表示对象的生命周期之间的区别,这是有效的 C99(和 C11)代码:

void foo(void) {
    int *p = 0;
again:
    if(p) {
        printf("%d\n", *p); /* n is not in scope here, but the object exists */
        *p = 0;
    }
    int n = 42;
    printf("%d\n", n);
    if(!p) {
        p = &n;
        goto again;
    }
}

输出三次42,当第二次到达初始化器时,n被重新初始化为42(并且不停留为0)。

C89 不会出现这样的问题(标签不能在声明之上);在 GNU89 中,混合声明和代码是允许的,尽管我从 documentation 中不清楚如果保证遵守 C99 生命周期规则。

此代码未定义(在所有 C 标准中):

void foo(void) {
    int *p = 0;
    for(int i=0; i<2; ++i) {
        int n = 42;
        if(p) { /* (*) */
            printf("%d\n", *p);
        }
        p = &n;
    }
}

在第二次迭代中,p 指的是第一次迭代的 n,在其生命周期之后,尽管 n 可能都位于相同的存储位置,并输出 42。注意,当 (*) 第二次到达时,行为是未定义的,读取无效指针是未定义的,不仅是 printf 调用中的间接寻址。

关于c - 在 if block 中声明变量的确切效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29454271/

相关文章:

c - 带指针的链表 C

JavaScript:当范围已经允许访问时,为什么 checkLeaf 需要参数?

c - 英特尔 icc : how to dump optimized code as C file

c++ - gcc8.2 和 (intel) icc19.0.1 之间的 constexpr 差异

compiler-errors - 为什么英特尔C编译器无法构建coreutils?

c - 如何使用 posix_memalign 在 c 中初始化结构

c - OpenGL 鼠标 "lock"

ruby - block 外部可访问的局部变量

javascript - 将新的变量作用域应用于对象

c - Printf 返回的是从 scanf 输入的错误数字