6.5.2.3结构和联合成员中C中严格混叠规则的例外情况



C99标准报价:

6.5.2.3

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

这种情况有一个例子:

// The following code is not a valid fragment because
// the union type is not visible within the 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);
}

我添加了一些更改:

#include <stdio.h>
struct t1 { int m; };
struct t2 { int m; };
union u
{
struct t1 s1;
struct t2 s2;
};
int foo(struct t1 *p1, struct t2 *p2)
{
if (p1->m)
p2->m = 2;
return p1->m;
}
int main(void)
{
union u u;
u.s1.m = 1;
printf("%dn", foo(&u.s1, &u.s2));
}

正如您所看到的,我已经将联合声明移到了外部,所以它在foo()中可见。根据标准的评论,这本应使我的代码正确,但对于clang 3.4和gcc 4.8.2,严格的混叠似乎仍然会破坏代码。

输出-O0:

2

输出-O2:

1

适用于这两个编译器。

所以我的问题是:

C真的依赖于并集声明来决定某些结构是否是严格混叠规则的例外吗?或者gcc/clang都有错误?

在我看来,这真的很糟糕,因为即使函数和并集都在同一个头中声明,这也不能保证并集在函数体的翻译单元中可见。

最重要的一点是,您的更改(向上移动并集)根本不会更改函数foo的定义。它仍然是一个接收不相关指针的函数。在您的示例中,传递的指针是相关的,而在其他地方可能会有所不同。编译器的目标是为最一般的情况服务。改变后的身体功能有所不同,原因尚不清楚。

您要问的问题是,在特定编译器中,对于某些命令行键,如何进行仔细的优化。它与内存布局无关。在正确的编译器中,结果应该是相同的。当两个不同的指针实际上指向内存中的同一位置时,编译器应该处理这种情况。

编译器识别到对聚合成员的访问就是对聚合本身的访问的一组情况纯粹是实现质量问题,并且标准不努力识别使用形式为aggregate.memberpointerToAggregate->member的非字符左值不会违反6.5p7的任何情况。一个编译器如果不能处理定义的至少一些情况,那么它的质量就会很低,以至于毫无用处,但标准并没有努力禁止一致但无用的实现。

如果公共初始序列成员具有字符类型,那么6.5p7将定义访问它的行为,而不管它是否是完整声明可见的并集的公共初始序列的成员。如果它没有字符类型,那么如果通过字符类型或memcpy/memmove的左值执行,或者在目标具有堆持续时间并且用于读取的最终类型与用于写入的类型匹配的情况下,访问将仅在6.5p7下定义。

有许多迹象表明,高质量编译器应该认识到,指向一种结构类型的指针可能被用来访问另一种结构的CIS成员。无法识别任何其他指示的编译器可能会受益于将包含这两种类型的完整并集声明的存在视为此类指示。这样做可能会不必要地阻止一些有用的优化,但与完全禁用基于类型的别名分析相比,仍然允许更多的优化。

相关内容

  • 没有找到相关文章

最新更新