C语言中的空结构体

3

http://c0x.coding-guidelines.com/6.7.2.1.html:

如果 struct-declaration-list 不包含命名成员,其行为是未定义的。 这是否意味着以下内容是非法的?
struct C { };

或者这是什么意思?

我在我的C库绑定中使用了一个指向空结构体(实际上是空的Ada记录)的Convention=>C Ada指针来代替void*(因为Ada中没有void*)。我想知道这是否不正确。

我有什么误解?


请参见“不要将没有非零大小字段的结构体传递或返回给extern(C)函数。根据C11 6.7.2.1p8,这是未定义的行为。”,网址https://dlang.org/spec/struct.html

struct C{ };甚至无法匹配标准化语法。 struct C {_Static_assert(1,"");};可以( _Static_assert是不必要的丑陋语法),但如果结构体内没有命名成员,则会产生未定义行为 (http://port70.net/~nsz/c/c11/n1570.html#6.7.2.1p8)。 - Petr Skocik
2个回答

6
很少有在C中是非法的,意味着你不能这样做。相反,许多事情没有被C标准定义,这意味着如果你这样做了,C标准不会说明会发生什么。没有命名成员的结构就是这样一件事情。
C 2018 6.7.2.1 8部分地说:
如果struct-declaration-list不包含任何具名成员,直接或通过匿名结构或匿名联合体,则行为未定义。
如果你需要一个作为“未知类型”的东西,而又不想使用void,那么你可以声明一个结构,其标签已知但其内容不知道,如下所示:
struct foo;

你可以使用指向这种结构体的指针(例如,你可以定义一个指针struct foo *p;),但编译器不知道这种结构体的大小或内容,因此它不能帮助你分配它们、管理它们的数组,或以其他方式使用它们,除了通过传递指针并询问外部程序(这些程序知道结构体的内容)。

通常,在一个模块中的C例程和另一个模块中的C例程之间进行操作时:

  • 在一个不知道该结构体内容的源模块中,你将使用一个指针(struct foo *)。
  • 另一个源模块(通常是某种软件库)将知道该结构体的内容。它会在自己的源代码中用struct foo { /*各种元素*/ }来定义完整的结构体,并为第一个模块执行该结构体的服务。
  • 在两个这样的C模块之间,C标准的规则将定义其行为。

由于你正在C和Ada之间进行接口交互,所以这些交互的规则必须由你的C和Ada实现提供。


嗯,好吧,如果根本没有定义,为什么他们说 sizeof(struct C)==1 对于 struct C { }; - porton
2
@porton:他们是谁?我猜你在问你链接中的页面(https://dlang.org/spec/struct.html)。它说g++和clang++会为`sizeof`产生1。请记住,在C语言中很少有非法的事情。编译器可以扩展语言。由于C标准没有定义空结构体的行为,编译器可以定义发生了什么 - 只要您使用该编译器即可。 - Eric Postpischil
@porton • struct C { }; 不是指“根本没有定义”,而是表示它已经被定义了。struct C; 是声明但未定义,这就是为什么 struct foo* ptr; 能够工作的原因。一个空结构体具有非零大小,因为要有地址需要非零大小;可以将其视为填充字节和/或对齐字节。 - Eljay

1

从编写 Ada 绑定到 C 库的角度来看,您使用的 Ada Convention-C 访问类型来表示 void* 并不重要。您永远不会对此类型的对象执行任何操作,只需将其传递给导入的 C 函数即可。我通常使用

type Void_Ptr is access all Integer;
pragma Convention (C, Void_Ptr);

我认为你是错的。整数和结构体的对齐方式可能不同。 - porton
各种类型的对齐方式与void*的定义无关。 - Jeffrey R. Carter
我试过使用void*来尝试不同类型的对齐方式,但是为了取消引用它,需要大量的转换。现在我正在考虑使用空结构体并使用malloc来填充它,这可行吗? - R1S8K
如果您正在使用Ada绑定到C库并分配或取消引用void *,那么您正在做错事。 - Jeffrey R. Carter

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