0

我在reddit上问了一个问题。问题实际上是为什么自动对象(没有 VLA)的生命周期的起点在对象的范围之前?
我得到了答案,这可以作为我的问题的例子。

代码

#include <stdio.h>

int main(void) {
    int i = 0;
back:
    printf("i = %d", i);
    int j;
    if (i < 5) {
        if (i == 0)
            j = 0;
        j += i;
        printf(", j = %d\n", j);
        i++;
        goto back;
    }
    printf("\n");
}

输出

i = 0, j = 0
i = 1, j = 1
i = 2, j = 3
i = 3, j = 6
i = 4, j = 10
i = 5

但是有人说“当它的声明达到时,它的值j变得不确定,......”因为 C17 6.2.4p6(用我的粗体字)

对于这样一个没有可变长度数组类型的对象,它的生命周期从进入与其关联的块开始,直到该块的执行以任何方式结束。(进入封闭的块或调用函数会暂停,但不会结束当前块的执行。)如果递归地进入块,则每次都会创建对象的新实例。对象的初始值是不确定的。如果为对象指定了初始化,则在执行块时每次到达声明或复合文字时都会执行它;否则,每次达到声明时,该值变得不确定。

仅转到当前块中的标签不同于在递归函数或迭代语句中重复整个关联块。从这个角度来看,我认为j不受规则的影响(但这只是我的想法)。

顺便说一句,我认为无论我们以哪种方式思考,都存在矛盾。
- 如果int j;没有在每个序列中重新创建,则为重新定义。
- 如果int j;在每个序列中重新创建,j则应具有来自第二个序列的垃圾值。但输出显示为好像保留了“j”的值。

我一遍又一遍地阅读引用的部分文档,但无法理解。我错过了什么?

4

1 回答 1

3

重要的部分在后面:

如果为对象指定了初始化,[...]; 否则,每次达到声明时,该值变得不确定。

每次int j达到,值j变得不确定。j保留以前的值也是垃圾值的一个例子,因为它可以是任何值。如果您的代码依赖于该值,则意味着您的代码的行为未定义。

如果递归进入块,每次都会创建一个新的对象实例

“块”通常是以 a 开头并以 a{结尾的东西}。该段用于递归函数。

void func()
{ // block start
    int a; // new instance of object created each time, even recursively
    func(); // enter the block recursively
} // block end

您的代码中有一个块是用创建的,int main(void) {而代码中的另一个块是if (i < 5) {. if (i < 5) {不是递归输入的,而是输入,然后离开,然后再次输入,然后再次离开。

如果int j; 不是在每个序列中重新创建,而是重新定义。

引用那句话:

对于这样一个没有可变长度数组类型的对象[具有自动存储持续时间的对象,参见标准中的前一点],它的生命周期从进入与其关联的块开始,直到该块的执行以任何方式结束。

由 标识的对象的生命周期j从进入块开始,所以我认为它不是“重新创建的”。

int main(void) { // lifetime of "object j" starts here
                 // but you can't access it, no identifier is associated with it
back:;
    int j; // the identifier `j` is associated with "object j"
           // the value of the object associated with `j` is indeterminate
           // _each time_ execution flow gets here
    goto back;
} // lifetime of "object j" ends here (or at `return`)
  // scope of identifier `j` also ends here
于 2020-11-22T14:28:43.720 回答