Tipo punning y uniones en c

Actualmente estoy trabajando en un proyecto para construir un pequeño comstackdor solo por el gusto de hacerlo.

Decidí adoptar el enfoque de construir una máquina virtual extremadamente simple para apuntar, así no tengo que preocuparme por aprender los entresijos de los elfos, el ensamblaje de inteligencia, etc.

Mi pregunta es sobre tipear punning en C usando uniones. Decidí admitir solo enteros de 32 bits y valores flotantes de 32 bits en la memoria de la máquina virtual. Para facilitar esto, la “memoria principal” de la máquina virtual se configura así:

typedef union { int i; float f; }word; memory = (word *)malloc(mem_size * sizeof(word)); 

Entonces puedo tratar la sección de memoria como un int o un float dependiendo de la instrucción.

¿Es este tipo de técnica punning? Ciertamente, sería si usara las palabras ints como palabras de memoria y luego use un flotador * para tratarlos como flotadores. Mi enfoque actual, aunque sintácticamente diferente, no creo que sea semánticamente diferente. Al final, sigo tratando los 32 bits de la memoria como int o float.

La única información que pude encontrar en línea sugiere que esto depende de la implementación. ¿Hay una forma más portátil de lograr esto sin perder un montón de espacio?

Podría hacer lo siguiente, pero luego ocuparía más de 2 veces más memoria y “reinventaría la rueda” con respecto a las uniones.

 typedef struct { int i; float f; char is_int; } 

Editar

Tal vez no aclaré mi pregunta exacta. Soy consciente de que puedo usar un flotador o un int de una unión sin un comportamiento indefinido. Lo que busco es específicamente una forma de tener una ubicación de memoria de 32 bits que pueda usar de manera segura como int o flotar sin saber cuál fue el último valor establecido. Quiero dar cuenta de la situación en la que se utiliza el otro tipo.

Sí, el almacenamiento de un miembro de la unión y la lectura de otro es de tipo punning (suponiendo que los tipos son lo suficientemente diferentes). Además, este es el único tipo de punning de tipo universal (de cualquier tipo a cualquier tipo) que es oficialmente compatible con el lenguaje C. Se admite en un sentido que el lenguaje promete que, en este caso, se producirá realmente el tipo punning, es decir, que se realizará un bash físico de leer un objeto de un tipo como un objeto de otro tipo. Entre otras cosas, significa que escribir un miembro de la unión y leer otro miembro implica una dependencia de datos entre la escritura y la lectura. Sin embargo, esto aún le deja la carga de asegurarse de que el tipo de punting no produzca una representación de trampa.

Cuando utiliza punteros para el tipo punning (lo que generalmente se entiende como punning tipo “clásico”), el lenguaje establece explícitamente que, en general, el comportamiento es indefinido (aparte de reinterpretar el valor del objeto como una matriz de caracteres y otros casos restringidos) . Los comstackdores como GCC implementan la llamada “semántica de alias estricta”, lo que básicamente significa que el punning de tipo puntero basado en punteros podría no funcionar como se espera. Por ejemplo, el comstackdor podría (y lo hará) ignorar la dependencia de los datos entre las lecturas y escrituras punzadas con letras y reorganizarlas arbitrariamente, arruinando completamente su intención. Esta

 int i; float f; i = 5; f = *(float *) &i; 

puede ser fácilmente reorganizado en real

 f = *(float *) &i; i = 5; 

específicamente porque un comstackdor de alias estrictos ignora deliberadamente la posibilidad de dependencia de datos entre la escritura y la lectura en el ejemplo.

En un comstackdor de C moderno, cuando realmente necesita realizar una reinterpretación física de un valor de un objeto como valor de otro tipo, está restringido a los bytes de memcpy de un objeto a otro o al punning de tipo basado en la unión. No hay otras formas. Casting punteros ya no es una opción viable.

Siempre y cuando solo accedas al miembro ( int o float ) que se almacenó más recientemente, no hay ningún problema ni una verdadera dependencia de implementación. Es perfectamente seguro y está bien definido almacenar un valor en un miembro de la unión y luego leer ese mismo miembro.

(Tenga en cuenta que no hay garantía de que int y float sean del mismo tamaño, aunque están en todos los sistemas que he visto).

Si almacena un valor en un miembro y luego lee el otro, ese es el tipo punning. Citando una nota al pie en el último borrador del C11:

Si el miembro utilizado para leer el contenido de un objeto de unión no es el mismo que el último utilizado para almacenar un valor en el objeto, la parte apropiada de la representación del objeto del valor se reinterpreta como una representación del objeto en el nuevo tipo como descrito en 6.2.6 (un proceso a veces llamado “tipo punning”). Esto podría ser una representación trampa.