После нескольких часов экспериментов кажется, что ограничение ошибка в 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
в предварительную декларацию. В соответствии с заказом документов :
Это игнорируется, если содержимое структуры, объединения или перечислимого типа не определено в спецификаторе, в котором используется список спецификаторов атрибута, то есть в таких случаях, как struct attribute ((foo )) панель без открывающей скобки.
Потенциальный обходной путь может быть объявлен foo
следующим образом:
void foo(struct MyType __attribute__((may_alias)) *, int *b);
Однако это не очень хорошее решение, похоже, синтаксис может еще не поддерживаться :
Обратите внимание, что это не работает с большинством атрибутов; например, использование указанных выше атрибутов «align» и «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;
// ...
}
Это совсем не элегантно, но единственный способ, который работает на данный момент.