Законно ли е прехвърлянето на int указател към плаващ указател, ако имат същото подравняване?

Да кажем, че и int, и float са подравнени по 4 байта. Съгласно ISO C99 6.3.2.3 p.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
@The Paramagnetic Croissant 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 p.7 не споменава дерефериране, но казва, че ако два указателя имат различни alignment, ако вече е UB, за да конвертирате един в друг, така че на теория всичко може да се случи на този етап дори без дерефериране.   -  person user1042840    schedule 09.03.2015
comment
Ако 2 типа имат едни и същи изисквания за подравняване, отливането е ОК, независимо от размерите на шрифта съгласно C99 6.3.2.3 p.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 GB, може да има указатели към подравнени по думи обекти да бъдат 32 бита, въпреки че void* е 64 бита. Ако кодът съдържаше много препратки към int-подравнени обекти, такъв дизайн би могъл да намали наполовина количеството памет/кеш, необходимо за задържане на указатели, потенциално предлагайки значителна печалба в производителността.   -  person supercat    schedule 08.05.2015