繁体   English   中英

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

[英]Are C-structs with the same members types guaranteed to have the same layout in memory?

本质上,如果我有

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

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

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

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

几乎是的。 对我来说足够近了。

从n1516,第6.5.2.3节,第6段:

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

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

struct a {
    int x;
    int y;
};

struct b {
    int h;
    int k;
};

union {
    struct a a;
    struct b b;
} u;

如果您指定给ua ,标准表示您可以从ub读取相应的值。 根据这一要求,它延伸了合理性的界限,表明struct astruct b可以有不同的布局。 这样的系统将是病态的极端。

请记住,该标准还保证:

  • 结构永远不会陷阱表示。

  • 结构中字段的地址增加( ax总是在ay之前)。

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

但是,这很重要!

你改写了这个问题,

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

没有! 它非常明确地说它们不一样!

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节的警告。 显然,关于anywhere that a declaration of the completed type of the union is visibleanywhere that a declaration of the completed type of the union is visible的确切措辞存在争议,并且至少GCC没有按照书面形式实施 这里此处有一些切线的C WG缺陷报告,并附有委员会的后续评论。

最近我试图找出其他编译器(特别是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);
}

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

GCC: -1, clang: -1, ICC: 1但警告有关别名违规

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

GCC: -1, clang: -1, ICC: 1,没有警告

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

换句话说,C的这个方面是一个真正的雷区,你必须要警惕你的编译器正在做正确的事情,即使你遵循标准的字母。 更糟糕的是,因为直观的这样一对结构应该在内存中兼容。

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

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

这个例子如下:

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

 struct t1 { int m; }; struct t2 { int m; }; 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; /* ... */ return f(&u.s1, &u.s2);} } 

要求似乎是1.被别名的对象存储在一个union并且该union类型的定义在范围内。

对于它的价值,C ++中相应的初始子序列关系不需要union 一般而言,这种union依赖对编译器来说是一种极端的病态行为。 如果某种方式存在联合类型可能会影响一个内存模型,那么最好不要试图想象它。

我想的意图是内存访问验证程序(想想类固醇上的Valgrind)可以检查针对这些“严格”规则的潜在别名错误。

我想扩展@Dietrich Epp 的答案。 以下是来自 C99 的一段话:

6.7.2.1 第 14 点...指向联合 object 的指针,经过适当转换,指向其每个成员...反之亦然。

这意味着我们可以将 memory 从结构复制到包含它的联合:

struct a
{
    int foo;
    char bar;
};

struct b
{
    int foo;
    char bar;
};

union ab
{
    struct a a;
    struct b b;
};

void test(struct a *aa)
{
    union ab ab;
    memcpy(&ab, aa, sizeof *aa);

    // ...
}

C99 还说:

6.5.2.3 第 5 点为了简化联合的使用,提供了一项特殊保证:如果联合包含多个共享相同初始序列的结构......,并且如果联合 object 当前包含这些结构之一,则允许在可以看到联合类型的完整声明的任何地方检查其中任何一个的公共初始部分。 如果对应的成员具有兼容的类型,则两个结构共享一个共同的初始序列……对于一个或多个初始成员的序列。

这意味着在memcpy之后以下内容也是合法的:

ab.a.bar;
ab.b.bar;

该结构可以在单独的翻译单元中初始化,并且复制在标准库中完成(不受编译器的控制)。 因此, memcpy将逐字节复制struct a类型的 object 的值,并且编译器必须确保结果对两个结构都有效。 编译器只能生成从这两行对应的 memory 偏移量读取的指令,因此地址必须相同。

即使没有明确说明,我会说标准意味着具有相同成员类型的 C 结构在 memory 中具有相同的布局。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM