C语言中的空结构体

21

我有一个还没有成员的结构体,想知道是否有可能抑制我得到的警告:

warning: struct has no members

是否可以添加一个成员并保持结构体的sizeof为零?是否有其他解决方案?


为什么您需要大小为零? - user25148
2
因为我有一个私有接口(其中我有我的零大小结构,因为我没有实现某个功能),以及一个公共接口,其中我的一些私有结构变得不透明,只需要与私有结构具有相同的大小。 - claf
我的构建系统检查私有结构体的大小,并使用“char _opaque [SIZEOF_PRIVATE_STRUCT]”创建公共结构体。 - claf
7个回答

27

C语言中,空结构体的行为取决于编译器,而在C++中它是规范的一部分(这里有解释

C++
拥有一个空成员序列和基类对象的类是一个空类。完整的空类类型的对象和成员子对象应具有非零大小。

在C语言中情况就比较模糊了,因为C99标准有一些语言暗示真正的空结构体是不允许的(参见TrayMan的答案),但许多编译器确实允许它(例如gcc)。

由于这取决于编译器,因此在这种情况下很难获得真正可移植的代码。因此,使用非便携式方法来抑制警告可能是最好的选择。


那么没有可移植的方法来避免这个警告吗?(除了使用编译器特定的#pragma,例如#ifdef) - claf
此外,对于GCC诊断指示: “另外,虽然在源代码中任何位置放置这些指示符在语法上都是有效的,但它们唯一支持的位置是在定义任何数据或函数之前。” 这意味着我不能仅仅抑制这个警告,我是正确的吗? - claf
1
问题是关于 C 语言,不是 C++。 - unwesen
2
@unwesen(我指出它们之间的区别(因为许多人可能会认为它们是相同的),pragma相关内容与OP的问题有关。我的答案基本上是“这取决于编译器,抱歉”,并提供了一些具体的编译器建议和信息,以及为什么 - ShuggyCoUk
1
@claferrir - 如果你想保持结构体实际上的零大小(尽管这在任何情况下可能都不是可移植的),那么就不能这样做。抱歉。 - ShuggyCoUk
这在C 2011年有所改变,明确规定结构体必须至少有一个成员。C 2011(草案N1570)6.7.2.1表示:“不声明匿名结构或匿名联合的struct-declaration应包含struct-declarator-list。”由于其语法,struct-declarator-list必然具有至少一个名称,并且由于struct-or-union-specifier语法,它出现在该名称的声明中作为结构体的成员。 - Eric Postpischil

25

如果你只需要用于类型转换和函数参数的结构体符号,那么只需:

typedef struct _Interface Interface;

这将创建一个不透明类型的符号。


3
谢谢!我正在尝试在不同的编译器上测试我的项目,在Borland C ++中遇到了一个奇怪的编译错误。它告诉我使用空结构体的这个函数不是在它声明的类的成员中。我已经花了2.5个小时来解决这个问题。结果,我只需要在声明空结构体时去掉括号就可以了,就像你展示的那样,现在运行良好。真是该死的。谢谢! - leetNightshade

15

从技术角度来看,这甚至不是有效的C代码。

TrayMan在他的分析中有点偏差,是的6.2.6.1说:

除了位域之外,对象由一个或多个字节的连续序列组成,其数量,顺序和编码要么明确指定,要么实现定义。

但将其与6.2.5-20结合使用,该部分说:

——结构类型描述了一组顺序分配的非空成员对象(以及在某些情况下是不完整的数组),每个对象都具有可选指定的名称和可能不同的类型。

现在,您可以得出结构体将是一个或多个字节的结论,因为它们不能是空的。 您的代码显示警告,而相同的代码将在Microsoft的Visual Studio上编译时出现错误:

错误C2016:C要求结构体或联合至少有一个成员

所以简短的答案是否定的,没有通用的方法可以避免此警告,因为它告诉您正在违反C标准。 您必须使用特定于编译器的扩展来抑制它。


那么在这种情况下,唯一的解决方案是使用void*指针吗? void * var; 可以指向任何类型的变量,并且可以使用malloc初始化动态元素集合,这个解决方案怎么样?我已经尝试过了,但需要进行很多类型转换。 - R1S8K
有没有一种好的方式来初始化动态元素集合,使它们在类型上可能不同?我已经做了这个:tsk_args = malloc(2*sizeof(uint8_t)+1*sizeof(unsigned char*));其中 tsk_args 是指向 void 的指针。 - R1S8K

5
struct zero_information { int:0; };

上面的代码片段将从sizeof(struct zero_information)产生一个零值,但它可能会帮助您获得您所寻找的东西,因为分配给它的所有存储空间都是填充的(只能通过黑客访问,尽管我不记得从头顶上访问填充是否未定义行为)。

与unwind的解决方案中我的评论相同。 - Aconcagua
请问这里的int:0;是什么意思?能否解释一下? - Mehdi Charife
@MehdiCharife 这声明了一个宽度为0位的int类型的未命名成员。这些被称为位域,但最好避免使用(会破坏填充,使访问更快等)。 - undefined

5

C99标准在这个问题上有些模糊,但似乎表明一个空结构应该具有非零大小。

6.2.6.1 除了位域之外,对象由一个或多个字节的连续序列组成,其数量、顺序和编码要么明确指定,要么是实现定义的。


3
您好!以下是您需要翻译的内容:

是否可以添加一个成员并保持结构体大小为零?

不可以。顺便提一下,C++允许空结构体,但空结构体的sizeof()值始终为非零。

还有其他解决方案吗?

没有简单的解决方案。值得注意的是,在C中只有部分支持空结构体,在C99中是不允许的。

空结构体在C++中得到支持,但不同的编译器实现它们的结果也不同(对于sizeof和结构偏移量),特别是当您开始将继承引入其中时。


2
如果您不要求“过于严格”的遵守,您可能可以这样做:
struct empty {
  char nothing[0];
};

这是一个GCC扩展
我原本希望能够使用C99的“可变数组”特性,声明方式如下:
struct empty99
{
  char nothing[]; // This is a C99 "flexible array".
};

但是这并不起作用;他们要求至少有一个普通结构成员首先,它们不能是唯一的成员。

3
好的,GCC无论如何都接受空结构体...显然大小为0。另一方面,如果打开了严格模式(pedantic warnings + turning them into errors),它将在空结构体以及这个解决方案上失败。 - Aconcagua

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