GCC发出复合字面量和指定初始化警告,但Clang没有。

17

使用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)

1
之前没有人注意到这个问题,这就是全部。请注意,复合字面量周围的括号是不必要的。复合字面量似乎是错误的一部分;例如使用struct T t = {.b = &t.a };不会生成警告。一个单元素数组(复合字面量)也足够了。它似乎只是“仅在用复合字面量初始化后的元素之后”的问题。在c之后添加int d;,则不会有关于d的投诉。(在复合字面量之前或之后添加更多整数,仍然只有在复合字面量之后的一个元素会受到投诉。) - Jonathan Leffler
2
@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 的错误了。 - user2736738
1
请遵循GCC网站上的规则[https://gcc.gnu.org/],了解如何报告错误[https://gcc.gnu.org/bugs/] - 其中之一是您需要提交预处理源代码,因此最小化生成问题所需的标头可以减小错误报告的大小。这就像MCVE升级版! - Jonathan Leffler
1
即使这是标志的目的,其余成员变量也应该自动初始化,因此不需要发出警告,不是吗?@M. M - David Ranieri
1
我的代码也受到了这个烦人的 bug 的影响。不过请注意,你可以通过 #pragma GCC diagnostic ignored "-Wmissing-field-initializers" 在每个文件的基础上抑制警告,作为(临时)解决方法。 - Will
显示剩余11条评论
2个回答

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则经验主义地不会发出警告。

0
为什么会出现警告?
因为-Wmissing-field-initializers是由-Wextra设置的,而你在gcc调用中设置了后者。 -Wextra很挑剔,而-Wmissing-field-initializers甚至不是-Wall的一部分。
省略一些字段初始化但不是全部是一个错误的源头。在一个有数百个元素的数组/结构体中,你只初始化其中一些,那么人类仅凭代码就几乎无法理解这个问题。

1
这个警告在gcc中是有问题的,参见使用指定初始化器时的-Wmissing-field-initializer。正如你所看到的,即使不省略任何初始化器,仍然会触发警告。虽然这已经是5年前的事了,但gcc主干仍然存在相同的错误。 - Lundin
我无法确认这一点。将该行更改为 struct T t = {.b = ((int []){1, 1}), .c = 1 };,以便初始化 c,则警告消失了。 - emacs drives me nuts
然而,这个警告是不稳定的,不值得信任。有很多误报。 - Lundin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接