使用指向struct成员的指针时是否应用严格别名?

时间:2017-02-03 13:22:04

标签: c strict-aliasing

当两个参数部分重叠时,test_func以下代码段会在严格别名规则下触发未定义的行为吗?

这是第二个参数是第一个参数:

#include <stdio.h>

typedef struct
{
    //... Other fields
    int x;
    //... Other fields
} A;

int test_func(A *a, int *x)
{
    a->x = 0;
    *x = 1;
    return a->x;
}

int main()
{
    A a = {0};

    printf("%d\n", test_func(&a, &a.x));

    return 0;
}

允许编译器认为test_func只会返回0,这是基于A*int*不会别名的假设?所以*x无法覆盖会员?

2 个答案:

答案 0 :(得分:5)

Strict aliasing指的是指针转换为另一种指针类型,之后访问内容。严格别名意味着所涉及的指向类型必须兼容。这不适用于此。

然而,术语指针别名,意味着两个指针可以引用相同的内存。不允许编译器假设这是这种情况。如果它想要像您描述的那样进行优化,则可能需要添加将指针相互比较的机器代码,以确定它们是否相同。这本身会使功能稍慢。

为了帮助编译器优化这样的代码,可以将指针声明为restrict,它告诉编译器程序员保证指针不指向同一个内存。

使用gcc -O3编译的函数会产生以下机器代码:

0x00402D09  mov    $0x1,%edx

这基本上意味着用&#34;将a.x设置为1&#34;来替换(内联)整个函数。

但是如果我将你的功能重写为

int test_func(A* restrict a, int* restrict x)
{
    a->x = 0;
    *x = 1;
    return a->x;
}

并使用gcc -O3进行编译,它确实返回0.因为我现在告诉编译器a->Xx没有指向同一个内存,所以可以假设{ {1}}不会影响结果并跳过行*x = 1;或在行*x = 1;之前对其进行排序。

限制版本的优化机器代码实际上会跳过整个函数调用,因为根据初始化它知道该值已经为0。

这当然是一个错误,但程序员应该责怪它,因为不小心使用a->x = 0;

答案 1 :(得分:4)

这不违反严格的别名。严格别名规则说(简化)您只能使用兼容类型的左值表达式来访问对象的值。在这种情况下,您要访问的对象是x的{​​{1}}变量的成员main。该成员的类型为a。用于访问它的表达式(int)也具有类型*x。所以没有问题。

您可能会将int的严格别名混淆。如果您在其中一个指针参数的声明中使用了restrict关键字,则代码将无效,因为restrict阻止您使用不同的指针来访问同一个对象 - 但这是一个不同的问题而不是严格的别名。