将全零分配给 C 中的嵌套结构(GCC 版本 < 5)

Posted

技术标签:

【中文标题】将全零分配给 C 中的嵌套结构(GCC 版本 < 5)【英文标题】:Assignment of all-zeroes to a nested struct in C (GCC versions < 5) 【发布时间】:2015-08-16 05:20:57 【问题描述】:

我在 C99 中有一个嵌套结构(我使用 GCC 4.8.3 和 -std=gnu99 -Wall)。

struct X

  struct
  
    int p;
    int q;
  
  a;

  struct
  
    int m;
    int n;
  
  b;

  int c, d, e;
;

我想为它定义一个全零的“默认值”。一种方法是明确指定每个字段的值,但我想使用快捷方式 0 作为默认值。这被称为“通用零初始化器” - 将其分配给变量将使该变量的所有字段归零。

但是,如果我尝试这样做:

struct X x =  0 ;

我收到warning: missing braces around initializer,然后收到有关缺少 X 字段的初始值设定项的进一步警告。

通常,归零x 不是问题。我知道其他选项,例如memset(),并使用静态变量的自动初始化为全零。这个问题是关于通用零初始化器,以及为什么它会意外生成警告。

为什么上面的内容会产生警告,而看起来应该没问题?

【问题讨论】:

如果你想分配零,试试memset(&amp;variable, 0, sizeof variable); Yes memset() 允许您更改结构而无需修改memset() @pmg 谢谢,但你会从我的问题中看到,我已经考虑过了,但它不适合。 @pmg 先生,您说的很对,但不是 1) OP 已经知道这一点并且 2) 他只希望初始化一个特定的 nested 成员? #define X_DEFAULT(var) memset(&amp;var, 0, sizeof var) 【参考方案1】:

为每个结构级别使用0

struct X 
  struct 
    int p;
    int q;
   a;

  struct 
    int m;
    int n;
   b;

  int c, d, e;
;

// struct X x =  0 ;
struct X x =   0 ,  0 , 0 ;

我现在看到这与“GCC 版本

【讨论】:

是的,这行得通,但是实际的嵌套结构有很多层次,而且它的声明是不断变化的。鉴于此,让这些零初始化器保持最新状态将是一件非常痛苦的事情。 @Rattus Ex Machina 代码可以将struct X g_0; 放在全局内存中(在程序启动时归零),然后在需要归零的struct X 的地方使用struct X x = g_0; 是的,当然也可以。但是,我试图在问题中找到一些不同的东西,正如你所见,我刚刚更新了它。这不应该是新问题的答案,如果我需要更多地解决这个问题......让我看看。【参考方案2】:

(我将根据我从其他人的贡献中学到的知识自己回答这个问题。)

简答

简而言之,这是 5.1 版修复的 GCC 错误:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119

早期版本的解决方法

分配给非嵌套结构(仅包含内置类型的结构)时不会出现此问题。因此,一个可用的解决方法是对 GCC 4.8.3 中有效且无警告的父结构的任何字段进行赋值。然后根据标准将剩余字段清零。例如:

struct X x =  .a =  0  ;

另见Why is the compiler throwing this warning: "missing initializer"? Isn't the structure initialized?

【讨论】:

我不确定这是否是重复的。我的意思是,它不是,因为它特定于版本 【参考方案3】:

首先,memset0 实现不同的东西。在这种情况下,memset 会将struct 的每个字节 设置为0,因此包括填充字节,因为sizeof 也将包括它们。0 将初始化每个member0,因为您没有初始化每个成员,所以剩余的成员将自动为零初始化。这种行为是完全有效且标准的 C。

如果 gcc 是 too restrictive ,请考虑添加 -Wno-missing-initializer 尽管 GCC 5.1 seems 已经放弃了。

【讨论】:

谢谢。不过,我有两个问题。嗯,一个问题... (a) 我喜欢这个警告,对于所有其他情况,所以我不想禁用它。并且,(b),哪个字段(这比问题更令人好奇)获得了我提供的显式 0 值,哪些设置为静态值?除了(a)之外,这至少感觉像是一个正式的解决方案。 啊,对不起,我误解了你回答的第二部分。查看您的seems,看起来5.1 可以在没有-Wno 标志的情况下很好地处理这个问题,对吧?我怀疑我可以升级我的编译器,但是“这在以后的版本中得到了修复”将是原始问题的正式解决方案。这是怎么回事,这里? 你能澄清一下“(b)”吗?关于你的第二个问题。正如我所说,这不是你的错,编译器也不是,完全:这只是一个警告,默认情况下它不存在。此外,据我所知,之所以显示它,是因为一般情况和实际零初始化之间存在/没有区别。 我不确定如何最终确定这一点 - 你的回答很有启发性,让我走上了正确的道路,但我试图在我提供给自己的关于那些事情的答案中更加明确使我困惑。不知道如何分配功劳,甚至不知道是否要删除重复项。 完全可以回答你的问题。但是,从这个问题来看,您真正想要实现的目标非常令人困惑;你最好澄清一下,省略一些不相关的细节。【参考方案4】:

怎么样:

#define DEFAULT_VALUE 0
#define DEFAULT(value, target) memset(&target, value, sizeof target)
#define DEFAULT_ZERO(target) DEFAULT(DEFAULT_VALUE, target)

struct A a;
DEFAULT_ZERO(a);

我们试图在这里真正通用,但是,我认为默认值不会改变。 memset 函数对结构中的字段一无所知。 memset 仅将一个字节(无符号字符)复制到指向的字符串的第一个 n 字节(&amp;target)。所以,我认为我们将 struct 的所有字节设置为与 0 不同的值是没有意义的。

【讨论】:

是的,使用宏有可能在实践中发挥作用。虽然上述情况不好,因为默认值的“零”性质仍然通过使用DEFAULT_ZERO 宏在源代码中定义。我当然可以有一个DEFAULT_A 宏,但最终结果不会很优雅。 你可以使用DEFAULT(value, target)来使用任何值; 我以为你想使用零作为默认值,所以我不明白这个问题。 “源代码中仍然定义了默认值”是什么意思?? 今天,默认值为零。明天,谁知道呢。这就是问题所在,我希望在标题中定义默认值为零。我很感激我可以使用存储在标题中的宏来做到这一点,我只是在 C99 中寻找最正确的方法。 你可以只做`#define DEFAULT_VALUE 0`。请注意,所有这些宏代码都可以存在于头文件中。它不需要在源文件中。

以上是关于将全零分配给 C 中的嵌套结构(GCC 版本 < 5)的主要内容,如果未能解决你的问题,请参考以下文章

嵌套结构中的指针在c中不是NULL

在结构中分配内存时出现不可预测的行为

gcc中的参数-I -L -l三者的区别

c++内存示例003calloc,malloc

C:如果调用顺序改变,使用结构的嵌套函数会破坏程序

嵌套结构名称可见性