严格别名和指向联合字段的指针

时间:2015-08-12 14:48:01

标签: c standards strict-aliasing

我对严格别名规则,工会和标准有疑问。假设我们有以下代码:

#include <stdio.h>

union
{
    int f1;
    short f2;
} u = {0x1};

int     * a = &u.f1;
short   * b = &u.f2;

int main()
{
    u.f1 = 1;
    *a += 1;
    u.f2 = 2;
    *b *= 2;

    printf( "%d %hd\n", *a, *b);

    return 0;
}

现在让我们看看它是如何运作的:

$ gcc-5.1.0-x86_64 t.c -O3 -Wall && ./a.out 
2 4
$ gcc-5.1.0-x86_64 t.c -O3 -Wall -fno-strict-aliasing && ./a.out 
4 4

我们可以看到严格别名会破坏依赖关系。此外,它似乎是一个正确的代码而不破坏严格别名规则。

  1. 与工会字段相比,是否存在与地址相关的对象与所有类型的工会成员兼容?
  2. 如果1为真,编译器应该如何处理指向工会成员的指针?这是标准中的一个问题,允许这样的编译器行为?如果没有 - 为什么?
  3. 一般来说,编译器使用正确代码的不同行为在任何情况下都是不允许的。所以它似乎也是一个编译器错误(特别是如果将地址转换为union字段将在函数内部,SA不会破坏依赖性)。

2 个答案:

答案 0 :(得分:3)

C标准规定明确允许通过联合进行别名化。

但请检查以下代码:

void func(int *a, short *b)
{
     *a = 1; 
     printf("%f\n", *b);
}

严格别名规则的目的是假定ab不是别名。不过,您可以拨打func(&u.f1, &u.f2);

要解决这个难题,常识解决方案是说工会必须避免严格别名规则的“绕过许可”仅适用于通过名称访问工会成员的时间。

标准没有明确说明这一点。可以认为“如果成员使用......”(6.5.2.3)实际上是指定“旁路”仅在按名称访问成员时发生,但它不是100%明确的。

然而,很难提出任何替代和自我一致的解释。一种可能的替代解释就是写func(&u.f1, &u.f2)导致UB,因为重叠的对象被传递给一个'知道'它没有收到重叠对象的函数 - 有点像restrict违规。

如果我们将第一个解释应用于您的示例,我们会说*a中的printf会导致UB,因为存储在该位置的当前对象是short,并且6.5 .2.3没有启动,因为我们没有按名称使用工会成员。

我根据您发布的结果猜测gcc正在使用相同的解释。

这已在此处讨论过,但我现在找不到该主题。

答案 1 :(得分:2)

C99技术勘误3通过第6.5.2.3节中的说明阐明了基于联合方法的打字:

  

如果用于访问union对象内容的成员不是   与上次用于在对象中存储值的成员相同,   值的对象表示的适当部分是   如上所述,重新解释为新类型中的对象表示   在6.2.6(一个有时被称为“类型双关语”的过程)。

请参阅1042至1044的here