经过几个小时的实验,这似乎是 GCC 的一个限制错误。较新的 GCC 没有这个问题。
1。前向声明,没有may_alias(错误行为)
下面是严格别名问题的最小演示:
#include <stdio.h>
struct MyType; // Forward declaration here, without may_alias.
void foo(struct MyType *, int *b);
struct MyType {
short a;
}; // Full definition here, without may_alias.
void f(struct MyType *my_type, int *b)
{
*b = 1;
my_type->a = 0;
if (*b == 1) {
printf("Strict aliasing problem\n");
}
}
int main(void) {
int b;
f((struct MyType *)&b, &b);
return 0;
}
用 GCC 5.4.0 编译它:
$ gcc -O2 -o main main.c
$ ./main
Strict aliasing problem
2。没有前向声明,没有may_alias(错误行为)
同上。
3。前向声明,may_alias(不会编译)
struct __attribute__((may_alias)) MyType; // Forward declaration here, with may_alias.
struct __attribute__((may_alias)) MyType {
short a;
}; // Full definition here, with may_alias.
用 GCC 5.4.0 编译它:
$ gcc -O2 -o main main.c
main.c:11:10: error: conflicting types for ‘foo’
void foo(struct MyType *my_type, int *b)
^
main.c:5:10: note: previous declaration of ‘foo’ was here
void foo(struct MyType *, int *b);
似乎 GCC 认为 struct MyType; 是一种不同的类型。但是,无论如何我都没有找到将may_alias 属性添加到前向声明中。根据the order doc:
如果结构体、联合体或枚举类型的内容没有在使用属性说明符列表的说明符中定义——即在诸如struct attribute(( foo)) bar 没有后面的左大括号。
一个潜在的解决方法是像这样声明foo:
void foo(struct MyType __attribute__((may_alias)) *, int *b);
但是,这不是一个好的解决方案,看起来像这样的语法might be not yet supported:
再次注意,这不适用于大多数属性;例如,尚不支持使用上面给出的“aligned”和“noreturn”属性。
虽然它可以编译,但 may_alias 属性不起作用:
$ gcc -O2 -o main main.c
$ ./main
Strict aliasing problem
4。没有前向声明,may_alias(好)
这是唯一可行的方法。
$ gcc -O2 -o main main.c
$ ./main
$
但是,就我而言,需要前向声明。我的解决方法是使用void * 而不是struct MyType *:
// In the .h file, no definition of MyType yet.
void foo(void *my_type, int *b);
// In the .c file, has the definition of MyType.
void foo(void *t, int *b)
{
struct MyType *my_type = t;
// ...
}
这一点都不优雅,但却是目前唯一可行的方法。