【问题标题】:Accessing first field of struct in a union of structs访问结构联合中结构的第一个字段
【发布时间】:2017-09-11 12:20:45
【问题描述】:

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

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);

对此我有 2 个问题:

  1. 是标准要求的联合体 始终将其内容放在同一个地址?
  2. 如果所有结构都具有相同的结构,它会起作用吗? 字段,只是名称不同?

【问题讨论】:

  • C 中没有“通用约定”。如果标准不保证什么,那取决于实现或者是 UB。该标准确实保证了这一点。实际的问题是:你为什么要关心?演员阵容是个坏主意。使用第一个元素。更好:将共同成员移动到一个拥抱struct
  • @Olaf 是的,我想我在考虑具体的实现是如何做到的;但由于我对标准感兴趣,所以我将其删除。没有它应该更清楚,谢谢。
  • 能否请反对者解释他们的立场?这是今天要问的更好的 C 问题之一,恕我直言。
  • @Bathsheba:这是我不得不承认我投错票的少数几个场合之一。我首先认为它太宽泛了(主要是因为现在删除了部分),但是看到你的回答,经过对我自己的问题的研究,我认为这确实是一个很好的问题。我现在将反对票改为赞成票。很抱歉犯了错误。

标签: c struct unions memory-alignment pointer-conversion


【解决方案1】:

structunion 的第一个元素保证与struct´/union 本身具有相同的地址值。显然它的类型不一样!

对于您的使用,您不需要演员表,实际上应该避免它:

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

所以你可以(见下文)简单地

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

(注意:您对未命名的union 成员的使用不是标准编译器。它是一个(非常有用的)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 的内部structs 中删除了name 成员,并将其移至外部struct。我也改了名字。 C 中唯一被广泛接受的命名约定是只对宏使用全大写。

【讨论】:

  • 感谢您的回答;我还是一个初学者,我正在尝试探索类型系统:) 可能无关,但我发现 C 类型系统真的很有趣;我首先在数学学士学位中学习了 C,然后转向函数和依赖类型语言,最后我通过 Rust 回到了 C;这有点冒险,但也很有启发性。
猜你喜欢
  • 2014-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 2019-06-05
  • 1970-01-01
  • 1970-01-01
  • 2014-06-20
相关资源
最近更新 更多