Aby dodać do odpowiedzi Dean, tutaj jest coś o ogólnych konwersjach wskaźnika. Zapomniałem, co to jest termin, ale wskaźnik do rzutowania wskaźnika nie wykonuje żadnej konwersji (w ten sam sposób, w jaki int jest float). Jest to po prostu reinterpretacja bitów, na które wskazują (wszystko dla korzyści kompilatora). "Nieniszcząca konwersja" Myślę, że tak było. Dane się nie zmieniają, tylko jak kompilator interpretuje to, na co się wskazuje.
np
Jeśli ptr
jest wskaźnikiem do object
, kompilator wie, że istnieje pole szczególna przesunięcie o nazwie type
typu enum type
. Z drugiej strony, jeśli ptr
jest rzutowany na wskaźnik na inny typ, cons_object
, ponownie będzie wiedział, jak uzyskać dostęp do pól każdego z nich z ich własnymi przesunięciami w podobny sposób.
Aby zilustrować wyobrazić układ pamięci dla cons_object
:
+---+---+---+---+
cons_object *ptr -> | t | y | p | e | enum type
+---+---+---+---+
| c | a | r | | object *
+---+---+---+---+
| c | d | r | | object *
+---+---+---+---+
Pole type
ma przesunięcie 0, car
wynosi 4, cdr
wynosi 8. Aby uzyskać dostęp do pola samochodu, wszystko kompilator musi zrobić, to dodać 4
do wskaźnika do struktury.
Jeśli wskaźnik został wrzucony do wskaźnika do object
:
+---+---+---+---+
((object *)ptr) -> | t | y | p | e | enum type
+---+---+---+---+
| c | a | r | |
+---+---+---+---+
| c | d | r | |
+---+---+---+---+
Wszystko kompilator musi wiedzieć, że istnieje pole o nazwie type
z offsetem 0. Cokolwiek jest w pamięci znajduje się w pamięci.
Wskaźniki nawet nie muszą być ze sobą powiązane. Możesz mieć wskaźnik do int
i przesłać go do wskaźnika do cons_object
. Jeśli masz dostęp do pola car
, to tak jak zwykły dostęp do pamięci. Ma pewne przesunięcie względem początku struktury. W tym przypadku to, co jest w tej lokalizacji pamięci, jest nieznane, ale to nieważne. Aby uzyskać dostęp do pola, potrzebne jest tylko przesunięcie i informacja ta znajduje się w definicji typu.
wskaźnik do int
punktów do bloku pamięci:
+---+---+---+---+
int *ptr -> | i | n | t | | int
+---+---+---+---+
odlewano do cons_object
palików:
+---+---+---+---+
((cons_object *)ptr) -> | i | n | t | | enum type
+---+---+---+---+
| X | X | X | X | object *
+---+---+---+---+
| X | X | X | X | object *
+---+---+---+---+
interesujące. ta wiedza znacznie uprości mój kod. dzięki. –
Należy wspomnieć, że jest to uzasadnione C z dobrze określonym zachowaniem, a nie "hack" lub inwokacja "niezdefiniowanego zachowania". –
re. object-in-cons_object - możesz również użyć makr, aby w tym przypadku rzutowanie było nieco bezpieczniejsze, np. #define OBJECT (x) i ((x) -> rodzic). To nie ma kosztu runtime (jest to adres pamięci równy x), ale oznacza, że nie rzucasz przypadkowo czegoś dziwnego. –