dbu*_*ush 5 c strict-aliasing unions language-lawyer
提出这个问题:
的C11标准规定,一个指向联合可以被转换为一个指针到它的每一个构件.从第6.7.2.1.1节开始:
联合的大小足以包含其最大的成员.最多一个成员的值可以随时存储在union对象中. 指向适当转换的union对象的指针指向其每个成员 (或者如果成员是位字段,则指向它所在的单位),反之亦然.
这意味着您可以执行以下操作:
union u {
int a;
double b;
};
union u myunion;
int *i = (int *)&u;
double *d = (double *)&u;
u.a = 2;
printf("*i=%d\n", *i);
u.b = 3.5;
printf("*d=%f\n", *d);
Run Code Online (Sandbox Code Playgroud)
但对于反向:在以上工会的情况下,可以在一个int *或double *安全地转换为union u *?请考虑以下代码:
#include <stdio.h>
union u {
int a;
double b;
};
void f(int isint, union u *p)
{
if (isint) {
printf("int value=%d\n", p->a);
} else {
printf("double value=%f\n", p->b);
}
}
int main()
{
int a = 3;
double b = 8.25;
f(1, (union u *)&a);
f(0, (union u *)&b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在这个例子中,指向int和double,都是其成员的指针union u被传递给union u *期望a的函数.将一个标志传递给函数以告诉它要访问哪个"成员".
假设,在这种情况下,访问的成员与实际传入的对象的类型匹配,上述代码是否合法?
我既编这对GCC 6.3.0 -O0和-O3无一不得到所需要的输出:
int value=3
double value=8.250000
Run Code Online (Sandbox Code Playgroud)
在此示例中,指向 int 和 double 的指针(两者都是联合 u 的成员)被传递到需要联合 u * 的函数。一个标志被传递给函数来告诉它要访问哪个“成员”。
假设像本例一样,访问的成员与实际传入的对象类型匹配,那么上面的代码合法吗?
您似乎将有关严格别名规则的分析重点放在联合成员类型上。然而,鉴于
union a_union {
int member;
// ...
} my_union, *my_union_pointer;
Run Code Online (Sandbox Code Playgroud)
,我倾向于认为,除了访问成员类型的对象之外,形式my_union.member和my_union_pointer->member表达式的表达式还访问类型对象的存储值。union a_union因此, ifmy_union_pointer实际上并不指向有效类型为 的对象union a_union,那么确实违反了严格的别名规则(就类型而言union a_union),因此行为是未定义的。
关于严格别名,从类型指针(例如&a)到包含该类型的联合指针不存在问题。它是严格别名规则 C17 6.5/7 的例外之一:
\n\n\n对象的存储值只能由具有以下类型之一的左值表达式访问:
\n
\n - 与对象的有效类型兼容的类型,/--/
\n - 包含一个的聚合或联合类型其成员中具有上述类型\n
因此,只要union包含int/ ,就严格别名而言就可以了double。指针转换本身也是明确定义的。
当您尝试访问内容时就会出现问题,例如较大int的double. 这可能是 UB 的原因有多种 - 我至少可以想到 C17 6.3.2.3/7:
\n\n\n指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐69),则行为未定义。
\n
非规范性脚注提供了更多信息:
\n\n\n\n69)一般来说,概念\xe2\x80\x9c正确对齐\xe2\x80\x9d是传递性的:如果指向类型A的指针对于指向类型B的指针正确对齐,\n反过来又对于指针正确对齐类型 C,则指向类型 A 的指针与指向类型 C 的指针正确对齐。
\n