这是一个理论问题,我知道如何明确地做到这一点,但我很好奇并且挖掘了标准,我需要第二对标准律师的眼睛.
让我们从两个结构和一个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;
}
Run Code Online (Sandbox Code Playgroud)
我们现在有一个草率的程序员不检查返回值:
void
x(void)
{
struct bar *b;
b = init_bar(&((struct foo){ .a = 42 }));
b->f->a++;
free(b);
}
Run Code Online (Sandbox Code Playgroud)
从我对标准的阅读中,除了可能解除引用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);
}
Run Code Online (Sandbox Code Playgroud)
代码做同样的事情,对吧?所以它也应该工作.但是仔细阅读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
Run Code Online (Sandbox Code Playgroud)
范围2完全在范围1内.请注意,在这种情况下,选择语句是整个if语句,而不仅仅是括号:
if( ... ){ ... }
Run Code Online (Sandbox Code Playgroud)
该语句中定义的任何内容都在范围2中.因此,如第三个示例所示,复合文字的生命周期(在范围2中声明)在结束时结束,如果括号(结束范围2),则该示例将导致如果函数返回非NULL,则为未定义的行为(如果err()未终止程序,则为NULL).
(注意我在if语句中使用了括号,即使第三个示例不使用它们.示例的那部分等同于此(6.8.2复合语句):
if ((b = init_bar(&((struct foo){ .a = 42 }))) == NULL)
{
err(1, "couldn't allocate b");
}
Run Code Online (Sandbox Code Playgroud)