基本上,如果我有
typedef struct {
int x;
int y;
} A;
typedef struct {
int h;
int k;
} B;
我有一个A a
,C标准是否保证((B*)&a)->k
与a.y
相同?
基本上,如果我有
typedef struct {
int x;
int y;
} A;
typedef struct {
int h;
int k;
} B;
我有一个A a
,C标准是否保证((B*)&a)->k
与a.y
相同?
struct a {
int x;
int y;
};
struct b {
int h;
int k;
};
union {
struct a a;
struct b b;
} u;
如果您给a赋值,标准规定您可以从b中读取相应的值。 基于此要求,建议认为struct a
和struct b
具有不同的布局是难以置信的。这样的系统将极端异常。a.x
始终在a.y
之前)。
- 第一个字段的偏移量始终为零。((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;
}
这是一个别名违规。
struct a
和struct b
的完整联合类型声明,符合规范且没有错误的编译器将会识别别名的可能性。一些编译器作者只想在适合自己的情况下遵守标准,即使标准保证它能够工作,他们也会破坏这样的代码;这仅意味着他们的编译器不符合规范。 - supercatx
在两个中具有相同的类型是不够的。问题在于a
的类型为struct a
,而你正在通过struct b
类型进行访问。这里有一个链接,展示了编译器如何基于别名进行优化:https://gcc.godbolt.org/z/7PMjbT 尝试移除-fstrict-aliasing
选项,看看生成的代码如何变化。 - Dietrich Epp// 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
,而不是包含类型的原始指针。然而,在我看来,这违背了使用union
的初衷。关于这个条款,我有一个问题 - 特别是它在C++中引起了显著的(也许是意外的?)排除 - 在这里:https://dev59.com/s1sW5IYBdhLWcg3wwZcP - underscore_dp=⁢ p->x=4;
将会调用未定义行为,因为它使用了类型为int
的左值来修改类型为struct foo
的对象,但标准的作者们期望编译器编写人员不会如此愚蠢,以至于假装他们不应该将其视为已定义。标准从未试图合理地指定任何特定平台和目的的实现所需支持的全部语义范围。荒谬的“有效类型”规则甚至不能... - supercat要求似乎是:1.被别名化的对象存储在一个“union”内,2.该“union”类型的定义在作用域内。The following is not a valid fragment (because the union type is not visible within function 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);} }
struct
的双关语。 (B) N685是对此的误读,应用于union
_type_和别名,强制实现大多数人不同意并忽略的复杂性。(C)引用的C++反射器显示了忽略N685的有意决定。 - underscore_dstruct 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点 为了简化联合的使用,提供了一个特殊保证:如果一个联合包含多个共享公共初始序列的结构体...,并且如果联合对象当前包含其中之一,则允许在任何可见联合完整类型声明的地方检查它们中任何一个的公共初始部分。如果对应成员具有兼容类型,则两个结构体共享公共初始序列...,对于一个或多个初始成员的序列。
这意味着在memcpy
之后以下内容也将是合法的:
ab.a.bar;
ab.b.bar;
memcpy
会逐字节复制类型为struct a
的对象的值,编译器必须确保结果对两个结构体都有效。
编译器除了生成读取这两行相应内存偏移量的指令外,无法执行其他任何操作,因此地址需要相同。