c - 头文件中的缩写结构重新定义是否符合标准?

标签 c language-lawyer

这是关于我正在处理的一些旧的(C89 之前的)代码。该代码的一部分是一个小型库,它在头文件中定义了这些结构:

// lib.h

struct data_node {
  const struct data_node *next;
  const struct data_node *prev;
  void *data;
};

struct trace_node {
  const struct trace_node *next;
  const struct trace_node *prev;
  unsigned int id;
  const char *file;
  int line;
};

const struct trace_node *get_trace(void);

源文件重新定义了那些相同的结构,如下所示:

// lib.c
// does *not* include "lib.h"

struct data_node {
  struct data_node *next;
  struct data_node *prev;
  void *data;
};

struct trace_node {
  struct trace_node *next;
  struct trace_node *prev;
  unsigned int id;
  const char *file;
  int line;
  struct data_node *syncData; /* not included in header file version */
};

它的工作方式与您预期的一样:syncData 字段对包含“lib.h” header 的客户端代码不可见。

背景

库维护 2 个内部列表:跟踪列表和数据列表。 syncData 字段使 2 个列表保持同步(看图)。

如果客户端代码可以访问 syncData 字段,它可能会破坏列表之间的同步。但是,跟踪列表可能会变得非常大,因此与其将每个节点复制到结构的较小版本中,不如返回内部列表的哨兵节点地址。

问题

我已经使用 -Wall、-Wpedantic 和 -Wextra 编译了它,但我无法让 gcc 提示它,无论是使用 -std=c99 还是 -std=c11。内存的十六进制转储显示了隐藏字段的字节,就在它们应该在的位置。

标准(6.2.7.1)的相关部分说:

Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.5 for declarators.46) Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: If one is declared with a tag, the other shall be declared with the same tag. If both are complete types, then the following additional requirements apply: there shall be a one-to-one correspondence between their members such that each pair of corresponding members are declared with compatible types, and such that if one member of a corresponding pair is declared with a name, the other member is declared with the same name. For two structures, corresponding members shall be declared in the same order. For two structures or unions, corresponding bit-fields shall have the same widths. For two enumerations, corresponding members shall have the same values.

根据您的阅读方式,可以认为兼容的结构定义仅限于具有相应的成对成员(没有其他成员),或者结构定义是如果它们确实有相应的成对成员,则这些成对符合要求,则兼容。

我不认为这是未定义的行为。在最坏的情况下,我认为它可能是未指定的。我应该重构它以使用 2 个不同的结构定义吗?这样做需要性能下降才能为内部列表中的每个节点分配一个新的公共(public)节点并复制公共(public)数据。

最佳答案

这有未定义的行为,并且有充分的理由。

首先,文中明确指出compatible struct 字段之间必须是一一对应的。因此,如果客户端和库访问同一个对象,则行为未定义。编译器无法检测到未定义的行为,因为这条规则是关于将来自两个单独编译的不同翻译单元的知识拼接在一起。这就是您看不到任何诊断信息的原因。

你的例子是特别糟糕的做法的原因是,甚至两个 struct 类型的大小都不一致,甚至可能不对齐。因此,访问此类对象的客户端会对优化机会做出错误的假设。

关于c - 头文件中的缩写结构重新定义是否符合标准?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48390788/

相关文章:

c - 在 C 编程中将结构体数组传递给函数

c++ - 在 Omnetpp 中从消息到数据包的类型转换

c - Unistd read() 最大尺寸

rust - Rust的确切自动引用规则是什么?

c++ - "const references"不应该实际称为 "references to const"吗?

c++ - 采用 const 参数的默认移动构造函数

c++ - 将 'override' 用于已使用 'typedef' 声明的函数

c - 没有得到任何输出

c - 指针数组 C - NULL POSITION

c - C 中不同 int 宽度的按位运算符是否定义良好?