这是一个理论问题,我知道如何毫不含糊地做到这一点,但我很好奇,并深入到标准,我需要第二双标准律师的眼睛。
让我们从两个结构和一个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/