【问题标题】:variables are declared and allocated with memory in which stage in C?变量是在 C 的哪个阶段声明和分配内存的?
【发布时间】:2022-01-24 04:39:43
【问题描述】:

我是 C 新手。我无法理解以下代码中的结果。我使用 goto 并跳转 int a[N] 数组和 int x 的声明。虽然x 没有初始化为10,但我仍然可以访问这些变量。

#include <stdlib.h>
#include <stdio.h>
#define N 4

void printArray(int a[], int length) {
    for (int i = 0; i < length; i++) {
        printf("%d, ", a[i]);
    }
    printf("\n");
}


int main(void) {

    goto done;
    
    int a[N];
    int x=10;
    printf("x=%d\n", x);

    done:
    for (int i = 0; i < N; i++) {
        a[i] = i;
    }
    printArray(a, N);
    printf("x=%d\n", x);

    return EXIT_SUCCESS;
}

结果

0, 1, 2, 3
x=0

我的问题:

为什么我可以访问这些声明已被跳转的变量? C语言中的变量是如何声明的?似乎变量声明不是逐行运行的。

【问题讨论】:

  • 您是否只是想知道 C 程序如何可能会以这种方式运行?或者你想让我们在 C 标准中查找这种行为,看看是否要求以这种方式表现?
  • @DavidGrayson 我对第一个问题很好奇。如果您能同时解释两者,那也很棒。非常感谢。

标签: c


【解决方案1】:

有关 C 标准的客观信息,请参阅 rici 的回答。

但我看到你在问这种行为是如何从 C 程序中实现的,并指出:

似乎变量声明不是逐行运行的。

事实上,大多数计算机语言都不是逐行运行的。几乎总是有某种预先发生的多行解析步骤。例如,即使是 Bash shell 语言也一次处理多行。当 Bash 找到 while 循环时,它似乎会进行额外的解析以确保在运行 while 循环中的任何代码之前找到 done 命令。如果您不相信我,请尝试运行此 Bash 脚本:

while [ 1 ]
do
  echo hi
  sleep 1
# file terminates before the expected "done" command that ends the loop

同样,C 编译器能够解析您的代码、检查其语法,并在您的程序甚至在您的任何函数中执行一行代码之前检查它的格式是否正确。当编译器看到您使用的是ax 时,它就知道这些东西是什么,因为您在上面声明了它们,即使程序的执行从未通过这些行。

您可以想象编译器可能工作的一种方式是(在检查代码的有效性之后)它将所有局部变量声明移动到函数的 top。因此,您的 main 函数在编译器的内部表示中可能如下所示:

int main(void) {
    int a[N];
    int x;
    int i;

    goto done;

    x = 10;
    printf("x=%d\n", x);

    done:
    for (i = 0; i < N; i++) {
        a[i] = i;
    }
    printArray(a, N);
    printf("x=%d\n", x);

    return EXIT_SUCCESS;
}

这实际上可能是一个有用的转换,它可以帮助编译器为您的函数生成代码,因为它可以让编译器在函数启动时轻松地在堆栈上为您的所有局部变量分配内存。

【讨论】:

    【解决方案2】:

    除了“可变长度数组 (VLA)”之外,自动 变量的“生命周期从进入与其关联的块开始,直到该块的执行以任何方式结束。” (§6.2.4)当程序通过声明时,初始化(如果有的话)稍后发生。如果程序不依赖于变量的初始化,跳过声明是合法的。但是不管初始化是否最终会发生,变量从你的程序进入块的那一刻起就存在,但是已经完成了。 (从外部跳入块也是合法的,也可能会阻止初始化。但只要你在块中,变量就存在了。)

    如果程序试图读取一个未初始化变量的值,它会收到一个 indeterminate 值。 (大多数编译器会尝试检测发生这种情况的可能性,但您需要启用警告才能查看报告。)

    将变量的生命周期“提升”到其封闭块的结果是程序的一部分存在变量但不可见(因为它的范围从定义的地方开始。)如果你保存变量的地址,然后跳转回程序的这个区域,就可以通过指针访问变量的值,可见作用域和生命周期是不同的。

    如果变量是 VLA,那么它的生命周期从声明开始,编译器将不允许您跳过声明。 VLA 无法初始化,因此您必须为您打算访问的 VLA 中的每个元素分配一个值。并非所有编译器都实现 VLA。您的示例没有显示 VLA,因为 N 是一个扩展为整数常量的宏。

    【讨论】:

      猜你喜欢
      • 2014-08-27
      • 2011-11-12
      • 1970-01-01
      • 2018-02-28
      • 2017-05-14
      • 1970-01-01
      • 1970-01-01
      • 2020-03-17
      • 2014-06-16
      相关资源
      最近更新 更多