Para agregar a la respuesta de Dean, aquí hay algo acerca de las conversiones de punteros en general. Olvidé cuál es el término para esto, pero un puntero al molde de puntero no realiza ninguna conversión (del mismo modo que int float). Es simplemente una reinterpretación de los bits que señalan (todo para el beneficio del compilador). "Conversión no destructiva" Creo que fue. Los datos no cambian, solo cómo el compilador interpreta a qué se apunta.
por ejemplo,
Si ptr
es un puntero a un object
, el compilador sabe que hay un campo con un desplazamiento en particular llamado type
de tipo enum type
. Por otro lado, si ptr
se convierte en un puntero a un tipo diferente, cons_object
, nuevamente sabrá cómo acceder a los campos del cons_object
cada uno con sus propios desplazamientos de forma similar.
Para ilustrar imaginar el diseño de memoria para un cons_object
:
+---+---+---+---+
cons_object *ptr -> | t | y | p | e | enum type
+---+---+---+---+
| c | a | r | | object *
+---+---+---+---+
| c | d | r | | object *
+---+---+---+---+
El campo type
ha compensado 0, car
es 4, es cdr
8. Para acceder al campo de coche, todo el compilador necesita hacer es añadir 4
al puntero a la estructura.
Si el puntero fue arrojado a un puntero a un object
:
+---+---+---+---+
((object *)ptr) -> | t | y | p | e | enum type
+---+---+---+---+
| c | a | r | |
+---+---+---+---+
| c | d | r | |
+---+---+---+---+
Todo el compilador necesita saber es que hay un campo llamado type
con desplazamiento 0. Lo que está en la memoria está en la memoria.
Los punteros ni siquiera tienen que estar relacionados en absoluto. Puede tener un puntero a int
y convertirlo en un puntero a cons_object
. Si tuviera acceso al campo car
, es como cualquier acceso normal a la memoria. Tiene un cierto desplazamiento desde el comienzo de la estructura. En este caso, lo que está en esa ubicación de memoria es desconocido, pero eso no es importante. Para acceder a un campo, solo se necesita el desplazamiento y esa información se encuentra en la definición del tipo.
un puntero a un int
puntos a un bloque de memoria:
+---+---+---+---+
int *ptr -> | i | n | t | | int
+---+---+---+---+
Casted a un cons_object
puntero:
+---+---+---+---+
((cons_object *)ptr) -> | i | n | t | | enum type
+---+---+---+---+
| X | X | X | X | object *
+---+---+---+---+
| X | X | X | X | object *
+---+---+---+---+
interesante. este conocimiento va a simplificar mucho mi código. Gracias. –
Debe mencionar que esto es C legítimo con un comportamiento bien definido, y no un "hack" o invocación de "comportamiento indefinido". –
re. object-in-cons_object: también puede usar macros para hacer que el encasillado sea un poco más seguro en este caso, p. #define OBJECT (x) y ((x) -> parent). Esto no tiene costo de tiempo de ejecución (es una dirección de memoria igual a x) pero significa que no accidentalmente lanzas algo extraño. –