Wtedy ten kompilator byłby po prosty wadliwy, słaby, bo ograniczony jakimiś gównianym standardem.
Standard jest jaki jest, a strict aliasing nie ogranicza kompilatora tylko daje możliwość lepszej optymalizacji kodu.
Ogranicza to co najwyżej programistę.
Wyobraź sobie kod:
void print_int(int);
void print_float(float);
int main()
{
int a = 42;
print_int(a);
float b = 3.14;
print_float(pi);
}
Kompilator oczywiście mógłby zapisać inta do jednej komórki RAM-u, odczytać go, przekazać do print_int, zapisać floata do drugiej komórki RAM, odczytać go i przekazać do print_float.
Ale nie musi. Może ten kod zamienić na coś takiego:
print_int(42);
print_float(3.14);
i jeżeli na danej architekturze parametry można przesyłać w rejestrach a nie np. na stosie, to te wartości mogą w ogóle nie mieć reprezentacji w RAM-ie. Jeżeli procesor ma osobne rejestry na liczby całkowite i zmiennoprzecinkowe, to będą to zupełnie różne rejestry.
Teraz wyobraź sobie, taki kod:
int a = 42;
int *pa = &a;
float *pf = (float*)pa;
*pf = 3.14;
print_int(a);
Co my tu mamy?
-
int a
równe 42. Na razie ten int może być w rejestrze albo być tylko umownym intem.
- Oho, pobierany jest wskaźnik na a, czyli a jednak musi być w pamięci, bo potrzebny jest adres.
- Reinterpret cast. Sam w sobie dozwolony.
- Zapisanie wartości float pod wskaźnikiem pf. Nie ma problemu.
- Wypisanie a. Ile wynosi a? Czy zmieniło się od pierwszego punktu? NIE! nigdzie nie ma instrukcji, która by zapisywała pod pa ani pod jakikolwiek inny int* z ewentualną indeksacją. Zatem nie musimy czytać wartości a z RAM-u. Na pewno wynosi nadal 42, więc możemy zrobić tutaj
print_int(42)
z wartością immediate albo z rejestru, dzięki czemu mamy szybszy kod.
ZONK.
Tak działają współczesne kompilatory. Tak jest zgodnie ze standardem języka C++: zapis do float* na pewno nie modyfikuje inta, i odwrotnie.
Kompilator nie może śledzić wszystkich wskaźników czy na pewno nie strzelasz sobie w stopę. Może w tak prostym przypadku mógłby, ale w przypadku ogólnym jest to niewykonalne (tzw. problem stopu).
Powyższy kod według standardu ma undefined behavior z powodu linii 4, gdzie następuje dereferencja wskaźnika float* mimo że w RAMie pod tym adresem znajduje się int.
Napisałem „z powodu linii 4” a nie „w linii 4”, bo UB dotyczy zachowania całego programu, a nie tylko w danej linii albo tylko od danej linii.