Quiero almacenar tipos de datos mixtos en una matriz. ¿Cómo podría uno hacer eso?
c
arrays
variant
mixed-type
chanzerre
fuente
fuente
Respuestas:
Puede hacer que los elementos de la matriz sean una unión discriminada, también conocida como unión etiquetada .
El
type
miembro se usa para mantener la elección de qué miembro de launion
debe usarse para cada elemento de la matriz. Entonces, si desea almacenar unint
en el primer elemento, haría:Cuando desee acceder a un elemento de la matriz, primero debe verificar el tipo y luego usar el miembro correspondiente de la unión. Una
switch
declaración es útil:Es responsabilidad del programador asegurarse de que el
type
miembro siempre corresponda al último valor almacenado en elunion
.fuente
Use una unión:
Sin embargo, deberá realizar un seguimiento del tipo de cada elemento.
fuente
Los elementos de la matriz deben tener el mismo tamaño, por eso no es posible. Podría solucionarlo creando un tipo de variante :
El tamaño del elemento de la unión es el tamaño del elemento más grande, 4.
fuente
Hay un estilo diferente de definir la unión de etiquetas (por cualquier nombre) que IMO hace que sea mucho más agradable de usar , eliminando la unión interna. Este es el estilo utilizado en el sistema X Window para cosas como eventos.
El ejemplo en la respuesta de Barmar le da el nombre
val
a la unión interna. El ejemplo en la respuesta de Sp. Utiliza una unión anónima para evitar tener que especificar.val.
cada vez que accede al registro de variantes. Lamentablemente, las estructuras y uniones internas "anónimas" no están disponibles en C89 o C99. Es una extensión del compilador, y por lo tanto inherentemente no portátil.Una mejor manera de IMO es invertir toda la definición. Haga que cada tipo de datos sea su propia estructura y coloque la etiqueta (especificador de tipo) en cada estructura.
Luego los envuelve en una unión de alto nivel.
Ahora puede parecer que nos estamos repitiendo, y lo estamos . Pero tenga en cuenta que es probable que esta definición esté aislada en un solo archivo. Pero hemos eliminado el ruido de especificar el intermedio
.val.
antes de llegar a los datos.En cambio, va al final, donde es menos desagradable. :RE
Otra cosa que esto permite es una forma de herencia. Editar: esta parte no es C estándar, pero usa una extensión GNU.
Up-casting y down-casting.
Editar: Una cosa que debes tener en cuenta es si estás construyendo uno de estos con inicializadores designados C99. Todos los inicializadores de miembros deben ser a través del mismo miembro de la unión.
El
.tag
inicializador puede ser ignorado por un compilador de optimización, porque el.int_
inicializador que sigue alias la misma área de datos. A pesar de que conocemos la disposición (!), Y que debe estar bien. No, no lo es. Utilice la etiqueta "interna" en su lugar (se superpone a la etiqueta externa, tal como queremos, pero no confunde al compilador).fuente
.int_.val
sin embargo, no alias la misma área porque el compilador sabe que.val
está en un desplazamiento mayor que.tag
. ¿Tienes un enlace para más discusión sobre este supuesto problema?Puede hacer una
void *
matriz, con una matriz separada desize_t.
Pero pierde el tipo de información.Si necesita mantener el tipo de información de alguna manera, mantenga una tercera matriz de int (donde int es un valor enumerado) Luego codifique la función que convierte dependiendo del
enum
valor.fuente
La unión es el camino estándar a seguir. Pero también tienes otras soluciones. Uno de ellos es el puntero etiquetado , que implica almacenar más información en los bits "libres" de un puntero.
Dependiendo de las arquitecturas, puede usar los bits bajos o altos, pero la forma más segura y portátil es usar los bits bajos no utilizados aprovechando la memoria alineada. Por ejemplo, en sistemas de 32 bits y 64 bits, los punteros
int
deben ser múltiplos de 4 (suponiendo queint
es un tipo de 32 bits) y los 2 bits menos significativos deben ser 0, por lo tanto, puede usarlos para almacenar el tipo de sus valores . Por supuesto, debe borrar los bits de la etiqueta antes de desreferenciar el puntero. Por ejemplo, si su tipo de datos está limitado a 4 tipos diferentes, puede usarlos como se muestra a continuaciónSi usted puede asegurarse de que los datos es de 8 bytes alineados (como para los punteros en sistemas de 64 bits, o
long long
euint64_t
...), tendrá un poco más por la etiqueta.Esto tiene la desventaja de que necesitará más memoria si los datos no se han almacenado en una variable en otro lugar. Por lo tanto, en caso de que el tipo y el rango de sus datos sean limitados, puede almacenar los valores directamente en el puntero. Esta técnica se ha utilizado en la versión de 32 bits de motor V8 Chrome , donde verifica el bit menos significativo de la dirección para ver si es un puntero a otro objeto (como dobles, enteros grandes, cadenas o algún objeto) o un 31 -bit valor con signo (llamado
smi
- entero pequeño ). Si es unint
, Chrome simplemente hace un desplazamiento aritmético a la derecha de 1 bit para obtener el valor, de lo contrario, el puntero se desreferencia.En la mayoría de los sistemas actuales de 64 bits, el espacio de direcciones virtuales sigue siendo mucho más estrecho que 64 bits, de ahí que el más significativos también se pueden usar como etiquetas . Dependiendo de la arquitectura, tiene diferentes formas de usarlas como etiquetas.ARM , 68k y muchos otros se pueden configurar para ignorar los bits superiores , lo que le permite usarlos libremente sin preocuparse por segfault ni nada. Del artículo de Wikipedia vinculado arriba:
En x86_64 aún puede usar los bits altos como etiquetas con cuidado . Por supuesto, no necesita usar todos esos 16 bits y puede omitir algunos bits para pruebas futuras
En versiones anteriores de Mozilla Firefox también utilizan pequeñas optimizaciones de enteros como V8, con los 3 bits bajos utilizados para almacenar el tipo (int, string, object ... etc.). Pero desde JägerMonkey tomaron otro camino ( Nueva Representación de Valor JavaScript de Mozilla , enlace de respaldo ). El valor ahora siempre se almacena en una variable de doble precisión de 64 bits. Cuando
double
es normalizado , se puede usar directamente en los cálculos. Sin embargo, si los 16 bits más altos son todos 1, lo que denota un NaN , los 32 bits inferiores almacenarán la dirección (en una computadora de 32 bits) en el valor o el valor directamente, se utilizarán los 16 bits restantes para almacenar el tipo Esta técnica se llama NaN-boxingo monjas de boxeo. También se usa en JavaScriptCore de WebKit de 64 bits y SpiderMonkey de Mozilla con el puntero almacenado en los 48 bits bajos. Si su tipo de datos principal es de punto flotante, esta es la mejor solución y ofrece un rendimiento muy bueno.Lea más sobre las técnicas anteriores: https://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations
fuente