Допустимо ли приведение указателя int к указателю с плавающей запятой, если они имеют одинаковое выравнивание?

Предположим, что оба 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, но я не понимаю, почему здесь важнее выравнивание, а не размер. Почему выравнивание имеет значение при преобразовании указателей и только при разыменовании?


person user1042840    schedule 09.03.2015    source источник
comment
Я считаю, что это UB, потому что float не соответствует ни одному из условий в вашем списке. Однако с профсоюзом можно возиться. (Или просто используйте -fno-strict-aliasing как нормальный человек.)   -  person tmyklebu    schedule 09.03.2015
comment
Но это UB только для разыменования * f?   -  person user1042840    schedule 09.03.2015
comment
Упс, да. То, что ты сделал, нормально. Но разыменование f недопустимо.   -  person tmyklebu    schedule 09.03.2015
comment
@ user1042840: Должен быть UB, только если вы получаете доступ к значению через f, да. Как бы то ни было, бессмысленно иметь этот указатель без разыменования. :)   -  person Ulfalizer    schedule 09.03.2015
comment
Круто, я просто хочу убедиться, что все понимаю. Еще одна вещь - было бы это приведение также законным, если бы float имел размер, отличный от int, но выравнивание по-прежнему было бы одинаковым для обоих типов?   -  person user1042840    schedule 09.03.2015
comment
@ Парамагнитный круассан OP, похоже, копирует только адрес i, но не его содержимое. Почему это UB?   -  person chux - Reinstate Monica    schedule 09.03.2015
comment
@chux извините, я имел в виду акт разыменования.   -  person The Paramagnetic Croissant    schedule 09.03.2015
comment
@chux: именно это и есть моя точка зрения - 6.3.2.3 стр.7 не упоминает разыменование, но говорит, что если два указателя имеют разные alignment, если это уже UB, чтобы преобразовать их друг в друга, теоретически все может произойти на этот этап даже без разыменования.   -  person user1042840    schedule 09.03.2015
comment
Если два типа имеют одинаковые требования к выравниванию, приведение в порядке, независимо от размера шрифта согласно C99 6.3.2.3 стр.7. Если типы имеют разные размеры (но одинаковые требования к выравниванию), арифметика указателей является проблемой, поскольку float *f = (float *) &i; &f[1] может быть UB. Конечным результатом является приведение одинаковых выровненных типов не вызовет столько проблем, сколько типичные операции next разыменования и арифметики указателей.   -  person chux - Reinstate Monica    schedule 09.03.2015
comment
Существуют (ред) машинные архитектуры, в которых простая загрузка невыровненного адреса в регистр, предназначенный для индексации памяти, может вызвать аппаратную ловушку. Но я не думаю, что какая-либо современная архитектура имеет такое ограничение.   -  person rep_movsd    schedule 09.03.2015
comment
@rep_movsd: некоторые системы могут использовать более короткое представление для указателей, которые, как известно, выровнены по более грубым границам. Например, компилятор для 64-битной платформы, пространство процесса которой будет ограничено 16 ГБ, может иметь указатели на объекты с выравниванием по словам 32-битными, даже если void* был 64-битным. Если бы код содержал много ссылок на int-выровненные объекты, такой дизайн мог бы сократить вдвое объем памяти / кеша, необходимый для хранения указателей, потенциально предлагая значительный выигрыш в производительности.   -  person supercat    schedule 08.05.2015