以下代码在使用gcc 4.1版和-O2标志编译时出错。它与gcc版本4.4,4.5等编译良好。
错误是:
warning: dereferencing type-punned pointer will break strict-aliasing rules
void foo(void **a = NULL);
int main()
{
int *a;
foo((void **)&a); //I get above error here
cout << "a[0] " << *a << endl;
cout << "a[1] " << *(a+1) << endl;
cout << "a[2] " << *(a+2) << endl;
return 0;
}
void foo(void **a)
{
int b[3];
b[0] = 10; b[1] = 20; b[2] = 35;
if(a != NULL) {
*a = (char *)malloc(20);
memcpy((char *)(*a), &b, 12);
}
}
现在要避免这种情况,我可以像下面给出的那样编程。这是避免这种警告的好方法吗?我可以在此代码中避免此警告。
void foo2(char **a = NULL);
int main()
{
char *a;
float c[3];
foo2(&a);
memcpy(&c, a, sizeof(c));
cout << "c[0] " << *c << endl;
cout << "c[1] " << *(c+1) << endl;
cout << "c[2] " << *(c+2) << endl;
return 0;
}
void foo2(char **a)
{
float c[3];
c[0] = 10.123; c[1] = 2.3450; c[2] = 435.676;
if(a != NULL) {
*a = (char *)malloc(sizeof(c));
memcpy((char *)(*a), &c, sizeof(c));
}
}
答案 0 :(得分:4)
编译器抱怨您使用强制转换int **
传递给接受void **
的函数,并且按照标准,编译器不需要检查通过void **
进行的写入可能会影响int *
类型的对象。
int *
和void *
是不同的类型,编译器不需要检查它们之间是否存在可能的别名。作为一个实际的具体例子,考虑:
int *a = &one_thing;
void **b = (void **)&a;
bar(a);
(*b) = &other_thing;
baz(a); // Here the compiler may assume `a` didn't change
另请注意,标准保证您可以将任何指向对象的指针转换为void *
并安全返回,但您的代码会将int**
转换为void**
并返回:这是不一样,也不保证是安全的。
memcpy
案例很有意思,因为正式函数需要void *
个值而void *
不能用于取消引用任何内容。然而,IIUC承认memcpy
一次读/写一个(无符号)char
因此编译器被迫假设在memcpy
调用任何类型的对象之后可能被写入的内容现在可以有一个新值(标准中有一条特殊规则,即char *
和usigned char *
可以为任何类型的值设置别名。)
所以是的,使用memcpy
你可以在类型系统周围做任何讨厌的事情,并且不允许编译器忽略/重新排序你的写操作。我已经阅读过编译器甚至检测到memcpy
调用并实际生成合理代码的地方,因此只有语法(而不是性能)才会变得非常糟糕。