Подтвердить что ты не робот

Объединенное литеральное время жизни и блоки

Это теоретический вопрос, я знаю, как сделать это однозначно, но мне стало любопытно и выкопано в стандарте, и мне нужна вторая пара стандартов адвокатов.

Начнем с двух структур и одной функции 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, и поток соединяется до возвращения функции, но я не хотел мутить воды,).

4b9b3361

Ответ 1

Вторая часть приведенного вами стандарта (6.8.4. Выражения выбора) говорит об этом. В коде:

{//scope 1

    if( ... )//scope 2
    {

    }//end scope 2

}//end scope 1

Область 2 целиком находится внутри области 1. Обратите внимание, что в этом случае выражение выбора является целым оператором if, а не только скобками:

if( ... ){ ... }

Все, что определено в этом утверждении, находится в области 2. Поэтому, как показано в третьем примере, время жизни составного литерала, объявленного в области видимости 2, заканчивается на закрытии, если скобка (конечная область 2), так что пример приведет к поведению undefined, если функция возвращает не-NULL (или NULL, если err() не завершает программу).

(Обратите внимание, что я использовал скобки в инструкции if, хотя третий пример их не использует. Эта часть примера эквивалентна этому (6.8.2 Compound statement):

if ((b = init_bar(&((struct foo){ .a = 42 }))) == NULL)
{
    err(1, "couldn't allocate b");
}