这是一个理论问题,我知道如何明确地做到这一点,但我很好奇并且深入了解标准,我需要第二对标准律师眼睛。
让我们从两个结构和一个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);
}
从我对标准的阅读中,除了可能解除引用NULL指针之外,这里没有任何错误。通过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
并且在函数返回之前连接了线程,但我不想让这个问题变得混乱涉及线程的水域。
答案 0 :(得分:5)
您引用的标准的第二部分(6.8.4选择声明)说明了这一点。在代码中:
{//scope 1
if( ... )//scope 2
{
}//end scope 2
}//end scope 1
范围2完全在范围1内。请注意,在这种情况下,选择语句是整个if语句,而不仅仅是括号:
if( ... ){ ... }
该语句中定义的任何内容都在范围2中。因此,如第三个示例所示,复合文字的生命周期(在范围2中声明)在结束时结束,如果括号(结束范围2 < / em>),如果函数返回非NULL,则该示例将导致未定义的行为(如果err()未终止程序,则该示例将为NULL)。
(请注意,我在if语句中使用了括号,即使第三个示例不使用它们。该示例的那部分等同于此( 6.8.2复合语句):< / p>
if ((b = init_bar(&((struct foo){ .a = 42 }))) == NULL)
{
err(1, "couldn't allocate b");
}