访问结构体联合体中的第一个结构体字段

3

我有三个结构体,它们共享第一个字段的类型和名称:

struct TYPEA {
  char *name;
  int x,y;   /*or whatever*/
};

struct TYPEB {
  char *name;
  float a[30]; /*or whatever*/
};

struct TYPEC {
  char *name;
  void *w,*z; /*or whatever*/
};

如果我没记错的话,结构体的第一个字段需要从结构体本身的起始地址开始。这让我想知道联合体是否也是如此:
union data {
  struct TYPEA;
  struct TYPEB;
  struct TYPEC;
};

union data *p = function_returning_a_sane_default_for_union_data();
printf("%s", (char*) p);

关于此事我有两个问题:

  1. 标准要求联合体的内容始终位于同一地址吗?
  2. 如果结构体仅在名称上不同,但所有字段都相同,是否可以使用?

1
在 C 语言中没有“通用约定”。如果标准没有保证某些东西,那就取决于实现或是未定义行为。标准确实保证了它。实际问题是:你为什么要关心这个?强制类型转换是一个不好的想法。使用第一个元素更好。更好的方法是:将公共成员移动到一个包含的 struct 中。 - too honest for this site
@Olaf,是的,我想我当时在考虑具体实现方式;但既然我对标准感兴趣,所以我把它删掉了。 没有它应该更清晰,谢谢。 - afiori
能否请那些给我点踩的人解释一下他们的立场?在我看来,这是今天提出的更好的 C 语言问题之一。 - Bathsheba
1
@Bathsheba:这是我不得不承认我误点了踩的少数几次之一。起初我认为它太宽泛了(主要是因为现在已经删除的部分),但看到你的回答后,我研究了自己的问题,我认为这确实是一个非常好的问题。我现在把踩改成了赞。对于我的错误表示抱歉。 - too honest for this site
1个回答

3
structunion的第一个元素保证具有与struct/union本身相同的地址值。但显然它的类型不同!对于您的使用,您不需要进行强制转换,实际上应该避免这样做:

6.5.2.3p6:为了简化联合的使用,提供了一个特殊的保证:如果一个联合包含多个共享公共初始序列的结构(见下文),并且如果联合对象当前包含其中一个结构,则可以在任何可见联合的已完成类型声明的地方检查它们的任何一个的共同初始部分。

因此,您可以(见下文)简单地进行操作。
printf("%s", p->name);

(注意:您使用未命名的联合成员不符合标准编译。这是一个(非常有用的)gcc扩展(-fms-extensions),至少也被MSVC支持。)
但是,您问题中的代码是错误的。您必须为每个联合成员命名或为每个成员声明类型说明符。尽管具有相同的第一个成员,它将无法正常工作,因为此类未命名成员的成员名称必须唯一(否则它们如何单独访问?)。因此,这并不真正奏效。您可以做的是:
union data {
    struct TYPEA typea;
    struct TYPEB typeb;
    struct TYPEC typec;
};

并且

printf("%s", p->typea.name);

即使struct当前包含TYPEB的值,也可以这样做。
另一种更清晰的方法是将union封装在一个struct中:
struct TypeA {
    int x,y;
};

...

struct data {
    char *name;
    union {
        struct TypeA;
        struct TypeB;
        struct TypeC;
    };
};

这里的代码使用了两个级别上的 gcc 扩展:对于外部 structunion,需要为所有可能的路径提供唯一的名称。如果您希望 100% 符合标准,请像上面那样为每个成员命名,并在访问时使用完整路径。
注意:我从 union 中的内部 struct 中删除了 name 成员,并将其移动到外部 struct 中。 我还更改了名称。在 C 中唯一被广泛接受的命名约定是仅针对宏使用全大写字母。

谢谢你的回答;我还是个初学者,正在尝试探索类型系统 :) 这可能与主题无关,但我觉得 C 的类型系统非常有趣;我最初在数学本科中学习了 C,然后转向了函数式和依赖类型语言,最后通过 Rust 回到了 C;这是一次冒险,但也非常有教益。 - afiori

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