【发布时间】:2016-01-19 15:27:09
【问题描述】:
这是一个理论问题,我知道如何明确地做到这一点,但我很好奇并深入研究了标准,我需要第二双标准律师的眼睛。
让我们从两个结构体和一个初始化函数开始:
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 并且线程在函数返回之前加入,但我不想通过涉及线程来混淆水域)。
【问题讨论】:
-
@Art 请不要说我的回答有一些不正确的陈述(没有双关语),我已经修复了它们。答案的结果还是一样的。查看修订:stackoverflow.com/posts/34880981/revisions
标签: c