Частичное присвоение структуры в C99+

Извините за плохой английский.

Предположим, код (C99 или новее):

typedef struct {
    int a, b;
} foo_t;

foo_t f = { .a = 1, .b = 2  };

f = (foo_t){ .b = 3 };

Что такое f.a сейчас? Говорит ли стандарт C что-нибудь об этом?

Я знаю, что для частичной инициализации стандарт гарантирует, что все неинициализированные элементы будут инициализированы "соответствующим нулем" (0 для целых чисел, 0.0 для чисел с плавающей запятой, NULL для указателей и т. д.). Но последний оператор не является инициализацией (как я понимаю), потому что f уже существует. Я смущен.


person Community    schedule 08.01.2013    source источник
comment
Я, конечно, не знаю, что говорит стандарт, но вы присваиваете только что созданной (анонимной) структуре, которая частично инициализирована. При назначении будут скопированы все члены, поэтому a будет равно 0.   -  person    schedule 08.01.2013
comment
@user707650 user707650 Я думаю, вы имеете в виду, что назначаете from недавно созданную (анонимную) структуру, которая частично инициализирована, верно? И присваивание этого частично инициализированного объекта к f. Другими словами, последняя строка создает временный foo_t с обнуленным элементом a, а затем присваивает его f. (Хороший улов! Сначала я предполагал, что последняя строка просто изменит поле b в f...)   -  person Razzle    schedule 01.12.2020


Ответы (2)


Пункт 6 в разделе 6.5.2.5 Составные литералы стандарта C99 гласит:

Значением составного литерала является значение безымянного объекта, инициализированного списком инициализаторов.

Безымянный foo_t частично инициализируется по тем же правилам, которые вы упомянули.

Последний оператор — это присваивание, а составной литерал — это безымянный объект, инициализированный списком инициализаторов. Это означает, что unnamed.a имеет нулевое значение, а f.a равно нулю после присваивания.

person hmjd    schedule 08.01.2013

В этом выражении:

f = (foo_t){ .b = 3 };

(foo_t){ .b = 3 } — это составной литерал, то есть lvalue типа foo_t, который частично инициализирован. Это все еще частичная инициализация, поэтому применяется то же правило:

C11 6.7.9/21:

Если в списке, заключенном в фигурные скобки, инициализаторов меньше, чем элементов или членов агрегата, или в строковом литерале, используемом для инициализации массива известного размера, меньше символов, чем элементов в массиве, оставшаяся часть агрегата должна быть инициализированы неявно так же, как объекты со статической продолжительностью хранения.

Только после инициализации составного литерала вы назначаете его f.

Это эквивалентно этому:

foo_t f = { .a = 1, .b = 2  };
foo_t c = { .b = 3 };
f = c;

Если вы не знакомы с составными литералами, в GCC есть хорошая документация по ним.

person netcoder    schedule 08.01.2013