这是一个理论问题,我知道如何毫不含糊地做到这一点,但我很好奇,并深入到标准,我需要第二双标准律师的眼睛。
让我们从两个结构和一个init函数开始:

struct foo {
    int a;
};
struct bar {
    struct foo *f;
};
struct bar *
init_bar(struct foo *f)
{
    struct bar *b = malloc(sizeof *b);
    if (!b)
        return NULL;
    b->f = f;
    return b;
}

我们现在有一个草率的程序员不检查返回值:
void
x(void)
{
    struct bar *b;

    b = init_bar(&((struct foo){ .a = 42 }));
    b->f->a++;
    free(b);
}

从我对标准的阅读来看,除了可能取消对空指针的引用之外,这里没有任何问题。通过struct foo中的指针修改struct bar应该是合法的,因为发送到init_bar的复合文本的生存期是包含它的块,它是整个函数x
但现在我们有了一个更加谨慎的程序员:
void
y(void)
{
    struct bar *b;

    if ((b = init_bar(&((struct foo){ .a = 42 }))) == NULL)
        err(1, "couldn't allocate b");
    b->f->a++;
    free(b);
}

代码也有同样的作用,对吧?所以它也应该起作用。但更仔细地阅读c11标准会让我相信这会导致未定义的行为。(引用我的话强调)
6.5.2.5复合文字
5复合文本的值是由
初始值设定项列表。如果复合文本出现在函数体之外,则对象
具有静态存储持续时间;否则,它具有与
封闭的块。
6.8.4选择声明
3选择语句是其作用域是其作用域的严格子集的块。
包围块。每个关联的子语句也是一个块,其作用域是严格的
选择语句范围的子集。
我读对了吗?if是块的事实是否意味着复合文本的生存期只是if语句?
(如果有人想知道这个人为的例子是从哪里来的,在实际的代码中init_bar实际上是pthread_create并且线程在函数返回之前被连接,但是我不想通过涉及线程来搅乱局面)。

最佳答案

你引用的标准的第二部分(6.8.4选择语句)说明了这一点。代码中:

{//scope 1

    if( ... )//scope 2
    {

    }//end scope 2

}//end scope 1

范围2完全在范围1内。注意,在本例中,选择语句是整个if语句,而不仅仅是括号:
if( ... ){ ... }

该语句中定义的任何内容都在范围2中。因此,如第三个示例所示,在作用域2中声明的复合文本的生存期在结束if括号(结束作用域2)处结束,因此,如果函数返回非null(如果err()不终止程序,则返回null),则该示例将导致未定义的行为。
(注意,我在if语句中使用了方括号,即使第三个示例没有使用它们。示例的这一部分相当于(6.8.2复合语句):
if ((b = init_bar(&((struct foo){ .a = 42 }))) == NULL)
{
    err(1, "couldn't allocate b");
}

关于c - 复合文字生命周期和if块,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34880638/

10-11 21:16