复合文字生命周期和if块

这是一个理论问题,我知道如何明确地做到这一点,但我很好奇并且挖掘了标准,我需要第二对标准律师的眼睛。

让我们从两个结构和一个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 bar的指针修改struct foo应该是合法的,因为发送到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并且在函数返回之前连接线程,但我不想通过涉及线程来init_bar水域)。

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

 {//scope 1 if( ... )//scope 2 { }//end scope 2 }//end scope 1 

范围2完全在范围1内。请注意,在这种情况下, 选择语句是整个if语句,而不仅仅是括号:

 if( ... ){ ... } 

该语句中定义的任何内容都在范围2中。因此,如第三个示例所示,复合文字的生命周期(在范围2中声明)在结束时以括号( 结束范围2结束 ,因此该示例将导致如果函数返回非NULL,则为undefined行为(如果err()未终止程序,则为NULL)。

(请注意,我在if语句中使用了括号,即使第三个示例不使用它们。该部分示例等同于此( 6.8.2复合语句 ):

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