Да кажем, че и 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, но не разбирам защо подравняването е най-важното тук, а не размерът. Защо подравняването има значение при конвертиране на указатели и само при дерефериране?
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