Это теоретический вопрос, я знаю, как сделать это однозначно, но мне стало любопытно и выкопано в стандарте, и мне нужна вторая пара стандартов адвокатов.
Начнем с двух структур и одной функции 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 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 приводит меня к мысли, что это приводит к поведению undefined. (акцент в кавычки мой)
6.5.2.5 Составные литералы
5 Значение составного литерала - это имя неназванного объекта, инициализированного список инициализаторов. Если составной литерал происходит вне тела функции, объект имеет статическую продолжительность хранения; в противном случае он имеет автоматическую продолжительность хранения, связанную с закрывающий блок.
6.8.4 Операторы выбора
3 Операция выбора - это блок, объем которого является строгим подмножеством сферы его охватывающий блок. Каждое связанное с ним субстрат также является блоком, масштаб которого является строгим подмножество области действия оператора выбора.
Я читаю это правильно? Означает ли факт, что if
является блоком, означает, что время жизни составного литерала является только выражением if?
(В случае, если кто-то задается вопросом о том, откуда пришел этот надуманный пример, в реальном коде init_bar
на самом деле pthread_create
, и поток соединяется до возвращения функции, но я не хотел мутить воды,).