复合文字生命周期和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"); }