当两个参数部分重叠时,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
无法覆盖会员?
答案 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->X
和x
没有指向同一个内存,所以可以假设{ {1}}不会影响结果并跳过行*x = 1;
或在行*x = 1;
之前对其进行排序。
限制版本的优化机器代码实际上会跳过整个函数调用,因为根据初始化它知道该值已经为0。
这当然是一个错误,但程序员应该责怪它,因为不小心使用a->x = 0;
。
答案 1 :(得分:4)
这不违反严格的别名。严格别名规则说(简化)您只能使用兼容类型的左值表达式来访问对象的值。在这种情况下,您要访问的对象是x
的{{1}}变量的成员main
。该成员的类型为a
。用于访问它的表达式(int
)也具有类型*x
。所以没有问题。
您可能会将int
的严格别名混淆。如果您在其中一个指针参数的声明中使用了restrict
关键字,则代码将无效,因为restrict
阻止您使用不同的指针来访问同一个对象 - 但这是一个不同的问题而不是严格的别名。