C/C++联合体中元素的内存位置

15

我在C语言中有一个如下的联合:

union AUnion {
  struct CharBuf {
    char *buf;
    size_t len;
  } charbuf;
  uint8_t num;
  double fp_num;
};

我的问题是,如果给定以下内容,我能否保证:

union AUnion u;

接下来的陈述是正确的:

&u == &u.num
&u == &u.fp_num
&u == &u.charbuf

也就是说,它们都始于存储变量u的内存段的开头。

对于使用gcc版本5.3.0-std=c11编译的这个C程序,上述内容是正确的:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

union AUnion {
    struct CharBuf {
        char *buf;
        size_t len;
    } charbuf;
    uint8_t num;
    double fp_num;
};

int main(void)
{
    union AUnion u;
    printf("%d\n", ((void*)&u) == ((void*)&u.charbuf));
    printf("%d\n", ((void*)&u.charbuf) == ((void*)&u.num));
    printf("%d\n", ((void*)&u.num) == ((void*)&u.fp_num));
}

当它打印时:

1
1
1

使用同一编译器将上述代码编译成C++11和C11,得到的输出结果相同。

但这是否是标准化行为?它是否未定义的行为?我能否在大多数C编译器上依赖这种行为?我是否也可以期望在C++编译器上得到这种行为?


C++ 并不会像 C 一样特别处理联合体。 - Uri Brecher
1
@UriBrecher 实际上有很多不同之处。 - M.M
1
@M.M:那么告诉我们C和C++中union的区别吧?如果你知道的话,列出许多不同之处! - Destructor
@Destructor 的内容太多了,无法放在评论中。如果你有疑问(或者找到了现有的问题),请发布一个问题。 - M.M
1
我想你是对的,C++在联合体中添加了一些C不支持的特性。然而,两种语言在将所有成员对齐到内存中相同位置方面表现出相同的行为。 - Uri Brecher
这里没有好的重复吗?我感觉这是一个常见问题。 - Lundin
2个回答

14
在C标准的6.7.2.1p16节中,保证了以下内容:联合体的大小足以容纳其成员中最大的一个;在联合体对象中只能存储一个成员的值;适当转换后,指向联合体对象的指针指向它的每个成员(如果成员是位域,则指向其中所属的单元),反之亦然。
因此,是的,您可以信任所有成员从union的地址开始(请注意,对于struct的第一个成员也是如此)。
C++标准包含了一个类似的句子,与C风格(即仅限于C风格成员)的union/struct有关,因为C ++允许将union传递给需要此布局的C函数。 C++标准中相关的部分是9.5节。
然而,注意标准简单类型(整数、浮点数)内部可能存在填充位,并且它们的内部可能会变化(字节顺序)。 您还可能违反严格别名规则(C:有效类型)。

这种做法假设指针转换永远不会改变内存地址,但在C++中并非如此。例如,多重继承就是一种证明方式。事实上,“适当转换”发生并不能证明成员在内存中的位置。 - Asteroids With Wings
@AsteroidsWithWings,您能否引用C++标准中的段落?我对C++不像对C那样熟悉,所以我没有想到´union´可以被继承。请注意,这不是关于其他类型的问题,只涉及C风格的成员,这排除了继承的成员(我的回答不够清楚吗?)。 - too honest for this site

9
根据我的经验,我会回答“是的”,尽管我已经查看了 C++14 standard 并且它甚至保证了这一点。(c++11 很可能也有相同的影响) 第9.5章说明: 联合体对象的所有非静态数据成员具有相同的地址 因此,您可以依赖这种行为。

这并没有涉及到联合体本身的地址。 - Asteroids With Wings

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