具有相同成员类型的C结构是否保证在内存中具有相同的布局?

具有相同成员类型的C结构是否保证在内存中具有相同的布局?,c,struct,casting,memory-layout,C,Struct,Casting,Memory Layout,基本上,如果我有 typedef struct { int x; int y; } A; typedef struct { int h; int k; } B; 我有A,C标准是否保证((B*)&A)->k与A.y相同?这种别名特别需要联合类型。C11§6.5.2.3/6: 为了简化联合的使用,我们提供了一个特殊的保证:如果联合包含多个共享公共初始序列的结构(请参见下文),并且如果联合对象当前包含其中一个结构,允许在任何地方检查其中任何一个结构的公共初始部分,

基本上,如果我有

typedef struct {
    int x;
    int y;
} A;

typedef struct {
    int h;
    int k;
} B;

我有
A
,C标准是否保证
((B*)&A)->k
A.y
相同?

这种别名特别需要
联合类型。C11§6.5.2.3/6:

为了简化联合的使用,我们提供了一个特殊的保证:如果联合包含多个共享公共初始序列的结构(请参见下文),并且如果联合对象当前包含其中一个结构,允许在任何地方检查其中任何一个结构的公共初始部分,以确保已完成的联合类型声明是可见的。如果对应的成员对于一个或多个初始成员的序列具有兼容的类型(对于位字段,具有相同的宽度),则两个结构共享一个公共初始序列

这个例子如下:

以下不是有效的片段(因为联合类型不是 在函数f中可见):

struct t1{int m;};
结构t2{int m;};
int f(结构t1*p1,结构t2*p2)
{
如果(p1->m<0)
p2->m=-p2->m;
返回p1->m;
}
int g(){
联合{
结构t1-s1;
结构t2s2;
}u;
/* ... */
返回f(&u.s1和&u.s2);}
}
要求似乎是1。被别名的对象存储在
联合体中
和2。该
联合
类型的定义在范围内

<>对于它的价值,C++中对应的初始子序列关系不需要<代码>联合< /COD>。一般来说,对于编译器来说,这种
union
依赖性将是一种极其病态的行为。如果联合类型的存在会以某种方式影响一个具体的内存模型,那么最好不要试图描绘它

我想其目的是让内存访问验证器(想想类固醇上的Valgrind)可以根据这些“严格”规则检查潜在的别名错误

具有相同成员类型的C结构是否保证在内存中具有相同的布局

几乎是的。离我足够近了

根据n1516第6.5.2.3节第6段:

。。。如果联合包含多个共享公共初始序列的结构…,并且联合对象当前包含这些结构中的一个,则允许在联合的完整类型声明可见的任何位置检查其中任何结构的公共初始部分。如果对应的成员对于一个或多个初始成员的序列具有兼容的类型(对于位字段,具有相同的宽度),则两个结构共享一个公共初始序列

这意味着如果您具有以下代码:

struct a {
    int x;
    int y;
};

struct b {
    int h;
    int k;
};

union {
    struct a a;
    struct b b;
} u;
如果指定给
u.a
,则标准规定您可以从
u.b
读取相应的值。考虑到这一要求,它扩展了合理性的界限,以表明
struct a
struct b
可以具有不同的布局。这样一个系统将是病态的极端

请记住,本标准还保证:

  • 结构永远不是陷阱表示

  • 结构中字段的地址增加(
    a.x
    始终在
    a.y
    之前)

  • 第一个字段的偏移量始终为零

然而,这很重要! 你重新措辞了这个问题

C标准是否保证
((B*)&a)->k
与a.y相同

不!它非常明确地指出,它们是不同的

struct a { int x; };
struct b { int x; };
int test(int value)
{
    struct a a;
    a.x = value;
    return ((struct b *) &a)->x;
}

这是一个别名冲突。

在其他回复上附加关于第6.5.2.3节的警告。显然,关于《法典》的确切措辞存在一些争论,即在任何地方都可以看到完整的工会类型声明,至少是这样。有一些切题的C工作组缺陷报告和委员会的后续意见

最近,我试图了解其他编译器(特别是GCC 4.8.2、ICC 14和clang 3.4)如何使用标准中的以下代码来解释这一点:

// Undefined, result could (realistically) be either -1 or 1
struct t1 { int m; } s1;
struct t2 { int m; } s2;
int f(struct t1 *p1, struct t2 *p2) {
    if (p1->m < 0)
        p2->m = -p2->m;
    return p1->m;
}
int g() {
    union {
        struct t1 s1;
        struct t2 s2;
    } u;
    u.s1.m = -1;
    return f(&u.s1,&u.s2);
}
//未定义,结果(实际上)可以是-1或1
结构t1{int m;}s1;
结构t2{int m;}s2;
int f(结构t1*p1,结构t2*p2){
如果(p1->m<0)
p2->m=-p2->m;
返回p1->m;
}
int g(){
联合{
结构t1-s1;
结构t2s2;
}u;
u、 s1.m=-1;
返回f(&u.s1和&u.s2);
}
GCC:-1,叮当声:-1,ICC:1并警告别名冲突

// Global union declaration, result should be 1 according to a literal reading of 6.5.2.3/6
struct t1 { int m; } s1;
struct t2 { int m; } s2;
union u {
    struct t1 s1;
    struct t2 s2;
};
int f(struct t1 *p1, struct t2 *p2) {
    if (p1->m < 0)
        p2->m = -p2->m;
    return p1->m;
}
int g() {
    union u u;
    u.s1.m = -1;
    return f(&u.s1,&u.s2);
}
// Global union definition, result should be 1 as well.
struct t1 { int m; } s1;
struct t2 { int m; } s2;
union u {
    struct t1 s1;
    struct t2 s2;
} u;
int f(struct t1 *p1, struct t2 *p2) {
    if (p1->m < 0)
        p2->m = -p2->m;
    return p1->m;
}
int g() {
    u.s1.m = -1;
    return f(&u.s1,&u.s2);
}
//全局联合声明,根据6.5.2.3/6的文本读数,结果应为1
结构t1{int m;}s1;
结构t2{int m;}s2;
联合大学{
结构t1-s1;
结构t2s2;
};
int f(结构t1*p1,结构t2*p2){
如果(p1->m<0)
p2->m=-p2->m;
返回p1->m;
}
int g(){
欧盟;
u、 s1.m=-1;
返回f(&u.s1和&u.s2);
}
GCC:-1,clang:-1,ICC:1但警告别名冲突

// Global union declaration, result should be 1 according to a literal reading of 6.5.2.3/6
struct t1 { int m; } s1;
struct t2 { int m; } s2;
union u {
    struct t1 s1;
    struct t2 s2;
};
int f(struct t1 *p1, struct t2 *p2) {
    if (p1->m < 0)
        p2->m = -p2->m;
    return p1->m;
}
int g() {
    union u u;
    u.s1.m = -1;
    return f(&u.s1,&u.s2);
}
// Global union definition, result should be 1 as well.
struct t1 { int m; } s1;
struct t2 { int m; } s2;
union u {
    struct t1 s1;
    struct t2 s2;
} u;
int f(struct t1 *p1, struct t2 *p2) {
    if (p1->m < 0)
        p2->m = -p2->m;
    return p1->m;
}
int g() {
    u.s1.m = -1;
    return f(&u.s1,&u.s2);
}
//全局联合定义,结果也应为1。
结构t1{int m;}s1;
结构t2{int m;}s2;
联合大学{
结构t1-s1;
结构t2s2;
}u;
int f(结构t1*p1,结构t2*p2){
如果(p1->m<0)
p2->m=-p2->m;
返回p1->m;
}
int g(){
u、 s1.m=-1;
返回f(&u.s1和&u.s2);
}
GCC:-1,clang:-1,ICC:1,无警告

当然,没有严格的别名优化,所有三个编译器每次都会返回预期的结果。由于clang和gcc在任何一个案例中都没有明确的结果,唯一真实的信息来自ICC对最后一个案例缺乏诊断。这也与标准委员会在上述第一份缺陷报告中给出的示例一致

换句话说,C语言的这一方面是一个真正的地雷