Составной литерал и указанное предупреждение инициализатора от GCC, но не от Clang

Компиляция с помощью gcc -std=c99 -Wextra этого фрагмента кода:

#include <stdio.h>

struct T {
    int a;
    int *b;
    int c;
};

int main(void)
{
    struct T t = {.b = ((int []){1, 1})};

    printf("%d\n", t.b[1]);
    return 0;
}

Дает мне предупреждение:

demo.c:11:12: warning: missing initializer for field ‘c’ of ‘struct T’ [-Wmissing-field-initializers]
     struct T t = {.b = ((int []){1, 1})};
            ^
demo.c:6:9: note: ‘c’ declared here
     int c;
         ^

Но назначенные инициализаторы должны обнулить остальные члены, даже если они опущены.

Почему предупреждение? (clang компилирует тот же кусок кода без предупреждений)

gcc version 6.3.0 20170516 (Debian 6.3.0-18) 
clang version 3.8.1-24 (tags/RELEASE_381/final)

person David Ranieri    schedule 03.03.2018    source источник
comment
Я получаю предупреждение, которое вы видите в GCC 7.3.0. Любопытно, что предупреждения для a при этом нет. Это несоответствие указывает на ошибку. Чтобы сообщить об этом команде GCC, вы можете отказаться от заголовка и printf() и сделать его общей функцией, а не main, чтобы ваш предварительно обработанный код был минимальным. Я тоже скомпилировал с -std=c11, а не с -std=c99.   -  person Jonathan Leffler    schedule 03.03.2018
comment
@JonathanLeffler, да, нет предупреждения для a, а также нет предупреждения, если составной литерал применяется к последнему члену struct   -  person David Ranieri    schedule 03.03.2018
comment
Очки для упоминания в отчете об ошибке.   -  person Jonathan Leffler    schedule 03.03.2018
comment
Никто не замечал его раньше — вот и все. Обратите внимание, что круглые скобки вокруг составного литерала не нужны. Составной литерал кажется частью ошибки; например, использование struct T t = {.b = &t.a }; не генерирует предупреждение. Одноэлементного массива (составной литерал) также достаточно. Также кажется, что это только элемент после инициализированного составным литералом. Добавьте int d; после c, и жалоб на d не будет. (Добавьте больше целых чисел до или после, и жалоба будет по-прежнему только на один элемент после составного литерала.)   -  person Jonathan Leffler    schedule 03.03.2018
comment
@JonathanLeffler .: Ну, он всегда жалуется на последний инициализатор назн, если после него есть член. И если после последнего десиг-инициализатора ничего нет - он не будет жаловаться. Например: struct T { int a; int *b; int c; int *e; /* int f; */ }; и struct T t = {.b = ((int []){1, 1}), .e=((int []){2,3})}; этого не будет. Но удалите комментарий, он будет больше о e, а не о b.   -  person user2736738    schedule 03.03.2018
comment
Я адаптировал код в вопросе к: struct T { int a; int *b; int c; }; struct T foo(int bar); struct T foo(int bar) { struct T t = {.b = (int[]){1}}; t.c = bar; return t; }, который можно предварительно обработать, не делая ничего, кроме добавления директив for #line (за исключением того, что line опущено). Что делает воспроизведение близким к минимальному. Я компилирую с gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -c diw71.c ('diw' — предупреждение назначенных инициализаторов; выберите свое имя).   -  person Jonathan Leffler    schedule 03.03.2018
comment
@JonathanLeffler.: Итак, какова общая процедура после возникновения ошибки? Также любопытно (я совершенно уверен, что кто-то есть), есть ли здесь кто-нибудь из команды gcc!. (S) он мог бы дать лучший обзор по этому поводу.   -  person user2736738    schedule 03.03.2018
comment
Следуйте правилам на сайте GCC на странице Как сообщать об ошибках — одна из них заключается в том, что вы должны отправить предварительно обработанный исходный код, поэтому минимизация заголовков, необходимых для создания проблемы, минимизирует размер отчета об ошибке. . Подумайте о MCVE на стероидах!   -  person Jonathan Leffler    schedule 03.03.2018
comment
@JonathanLeffler, спасибо, я сообщу об этом в gcc   -  person David Ranieri    schedule 03.03.2018
comment
@KeineLust.: Надеюсь, вы можете опубликовать в комментарии номер ошибки, чтобы мы могли следить за обсуждением.   -  person user2736738    schedule 03.03.2018
comment
@coderredoc, Джонатан: gcc.gnu.org/bugzilla/show_bug.cgi?id =84685   -  person David Ranieri    schedule 03.03.2018
comment
@KeineLust.: Круто. Все самое лучшее.   -  person user2736738    schedule 03.03.2018
comment
Итак, как мы думаем, он должен или не должен предупреждать? В конце концов, отсутствуют инициализаторы (не все поля имеют инициализаторы), и это вся цель этого предупреждающего флага.   -  person M.M    schedule 03.03.2018
comment
@М. M даже если это цель флага, остальные элементы должны быть инициализированы автоматически, поэтому нет необходимости выдавать предупреждение, не так ли?   -  person David Ranieri    schedule 03.03.2018
comment
На мой код тоже влияет эта досадная ошибка. Обратите внимание, что вы можете подавить предупреждения для каждого файла с помощью #pragma GCC diagnostic ignored "-Wmissing-field-initializers" в качестве (временного) обходного пути.   -  person Will    schedule 19.07.2019
comment
См. также эту ошибку GCC gcc.gnu.org/bugzilla/show_bug.cgi?id =82283   -  person Yann Droneaud    schedule 25.02.2020


Ответы (2)


Это похоже на "ошибку согласованности" gcc, вот соответствующий фрагмент кода в gcc/c/c-typeck.c

 7436   /* Warn when some struct elements are implicitly initialized to zero.  */
 7437   if (warn_missing_field_initializers
 7438       && constructor_type
 7439       && TREE_CODE (constructor_type) == RECORD_TYPE
 7440       && constructor_unfilled_fields)
 7441     {
 7442         bool constructor_zeroinit =
 7443          (vec_safe_length (constructor_elements) == 1
 7444           && integer_zerop ((*constructor_elements)[0].value));
 7445
 7446         /* Do not warn for flexible array members or zero-length arrays.  */
 7447         while (constructor_unfilled_fields
 7448                && (!DECL_SIZE (constructor_unfilled_fields)
 7449                    || integer_zerop (DECL_SIZE (constructor_unfilled_fields))))
 7450           constructor_unfilled_fields = DECL_CHAIN (constructor_unfilled_fields);
 7451
 7452         if (constructor_unfilled_fields
 7453             /* Do not warn if this level of the initializer uses member
 7454                designators; it is likely to be deliberate.  */
 7455             && !constructor_designated
 7456             /* Do not warn about initializing with ` = {0}'.  */
 7457             && !constructor_zeroinit)
 7458           {
 7459             if (warning_at (input_location, OPT_Wmissing_field_initializers,
 7460                             "missing initializer for field %qD of %qT",
 7461                             constructor_unfilled_fields,
 7462                             constructor_type))
 7463               inform (DECL_SOURCE_LOCATION (constructor_unfilled_fields),
 7464                       "%qD declared here", constructor_unfilled_fields);
 7465           }
 7466     }

Цель кода, по-видимому, состоит в том, чтобы предупредить, если какой-либо конструктор атрибута имеет незаполненное поле. Тот факт, что вы не получаете предупреждение об элементе «a», вероятно, является «ошибкой согласованности».

Если -Wextra предназначено для включения предупреждения об отсутствующих инициализаторах, то оно есть. Вопрос в том, должно ли «предупреждение об отсутствующих инициализаторах» исключать пропущенные атрибуты? Кажется, что gcc и clang не согласны с этим - и может быть, для них это нормально?

Возможно, это не тот ответ, который вы ищете... но надеюсь, что он поможет вам понять ситуацию. :). У команды GCC есть ошибка согласованности, но намерение их кода, кажется, предупреждает в этих случаях, тогда как clang, эмпирически, не будет.

person Andrew Atrens    schedule 11.02.2020

Почему предупреждение?

Потому что -Wmissing-field-initializers задается -Wextra, а вы устанавливаете последний в вызове gcc.

-Wextra разборчив, а -Wmissing-field-initializers даже не является элементом -Wall.

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

person emacs drives me nuts    schedule 31.01.2020
comment
Это предупреждение просто не работает в gcc, см. инициализаторы. Как видите, даже если не пропустить ни одного инициализатора, предупреждение все равно сработает. Это было 5 лет назад, но в багажнике gcc до сих пор обнаруживаются те же ошибки. - person Lundin; 31.01.2020
comment
Я не могу это подтвердить. Изменив строку на struct T t = {.b = ((int []){1, 1}), .c = 1 };, чтобы c тоже инициализировалось, предупреждение исчезнет. - person emacs drives me nuts; 31.01.2020
comment
Тем не менее, предупреждение непостоянно, и ему нельзя доверять. Много ложных срабатываний. - person Lundin; 31.01.2020