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

2024-03-05

我有一个关于严格别名规则、联合和标准的问题。假设我们有以下代码:

#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 为真,编译器应该如何处理指向联合成员的指针?是不是标准有问题allows这样的编译器行为?如果不是——为什么?
  3. 一般来说,编译器与正确代码的不同行为在任何情况下都是不可接受的。所以这似乎也是一个编译器错误(特别是如果将地址取到联合字段将在函数内部,SA不会破坏依赖性)。

C 标准规定明确允许通过联合使用别名。

但是请检查以下代码:

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

严格别名规则的目的是a and b应假定不是别名。不过你可以打电话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 使用相同的解释。

之前已经讨论过这个问题,但我现在找不到该线程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

严格别名和指向联合字段的指针 的相关文章

随机推荐