Предположим, что оба int
и float
выровнены по 4 байта. Согласно ISO C99 6.3.2.3 п.7:
Указатель на объект или неполный тип может быть преобразован в указатель на другой объект или неполный тип. Если результирующий указатель неправильно выровнен для указанного типа, поведение не определено.
В соответствии с этим следующий код не должен вызывать UB:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int i = 7;
float *f = (float *) &i;
exit(0);
}
Я заметил, что GCC не всегда хорошо ловит попытки нарушения правил строгого псевдонима, но здесь он может правильно предупредить, что разыменование * f will break нарушит правила строгого псевдонима, что является незаконным в соответствии с п. 6.5 p. 7:
Доступ к сохраненному значению объекта должен осуществляться только выражением lvalue, которое имеет один из следующих типов:
- тип, совместимый с эффективным типом объекта,
- квалифицированная версия типа, совместимого с эффективным типом объекта,
- тип, который является типом со знаком или без знака, соответствующим действующему типу объекта,
- тип, который является типом со знаком или без знака, соответствующим квалифицированной версии действующего типа объекта,
- тип агрегата или объединения, который включает в себя один из вышеупомянутых типов среди своих членов (включая, рекурсивно, член субагрегата или содержащегося объединения), или
- тип символа.
$ gcc -Wall -fstrict-aliasing -Wstrict-aliasing=2 so1.c -Wcast-align
so1.c: In function ‘main’:
so1.c:7:2: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
so1.c:7:9: warning: unused variable ‘f’ [-Wunused-variable]
Значит ли это, что этот код может вызывать UB только тогда, когда * f будет разыменован, и он на 100% верен в текущей форме?
РЕДАКТИРОВАТЬ:
Еще одна вещь - было бы это приведение также законным, если бы float имел размер, отличный от int, но выравнивание по-прежнему было бы одинаковым для обоих типов? Я думаю, что да, потому что это то, что говорит стандарт C, но я не понимаю, почему здесь важнее выравнивание, а не размер. Почему выравнивание имеет значение при преобразовании указателей и только при разыменовании?
float
не соответствует ни одному из условий в вашем списке. Однако с профсоюзом можно возиться. (Или просто используйте-fno-strict-aliasing
как нормальный человек.) - person tmyklebu   schedule 09.03.2015f
недопустимо. - person tmyklebu   schedule 09.03.2015f
, да. Как бы то ни было, бессмысленно иметь этот указатель без разыменования. :) - person Ulfalizer   schedule 09.03.2015i
, но не его содержимое. Почему это UB? - person chux - Reinstate Monica   schedule 09.03.2015alignment
, если это уже UB, чтобы преобразовать их друг в друга, теоретически все может произойти на этот этап даже без разыменования. - person user1042840   schedule 09.03.2015float *f = (float *) &i; &f[1]
может быть UB. Конечным результатом является приведение одинаковых выровненных типов не вызовет столько проблем, сколько типичные операции next разыменования и арифметики указателей. - person chux - Reinstate Monica   schedule 09.03.2015void*
был 64-битным. Если бы код содержал много ссылок наint
-выровненные объекты, такой дизайн мог бы сократить вдвое объем памяти / кеша, необходимый для хранения указателей, потенциально предлагая значительный выигрыш в производительности. - person supercat   schedule 08.05.2015