Articulo de referencia

Tipo Unión

En informática , una unión es un valor que puede tener múltiples representaciones o formatos dentro de la misma área de memoria , o una variable que puede contener dicha estruct...

En informática , una unión es un valor que puede tener múltiples representaciones o formatos dentro de la misma área de memoria , o una variable que puede contener dicha estructura de datos . Algunos lenguajes de programación admiten un tipo de unión para este tipo de datos . En otras palabras, un tipo de unión especifica los tipos permitidos que pueden almacenarse en sus instancias, por ejemplo, floaty int. A diferencia de un registro , que podría definirse para contener tanto un número de coma flotante como un entero, una unión solo puede contener uno a la vez.

Una unión puede visualizarse como un bloque de memoria que se utiliza para almacenar variables de diferentes tipos de datos. Una vez que se asigna un nuevo valor a un campo, los datos existentes se sobrescriben con los nuevos. El área de memoria que almacena el valor no tiene un tipo intrínseco (más allá de bytes o palabras de memoria), pero el valor puede tratarse como uno de varios tipos de datos abstractos , con el mismo tipo que el valor que se escribió por última vez en dicha área de memoria.

En teoría de tipos , una unión tiene un tipo suma ; esto corresponde a la unión disjunta en matemáticas.

Dependiendo del lenguaje y el tipo, un valor de unión puede usarse en algunas operaciones, como la asignación y la comparación de igualdad, sin conocer su tipo específico. Otras operaciones pueden requerir ese conocimiento, ya sea mediante información externa o mediante el uso de una unión etiquetada .

Uniones sin etiquetar

Debido a las limitaciones de su uso, las uniones sin etiquetar generalmente solo se proporcionan en lenguajes sin tipado o de forma no segura en cuanto a tipos (como en C ). Tienen la ventaja, frente a las uniones etiquetadas simples, de no requerir espacio para almacenar una etiqueta de tipo de datos.

El nombre "unión" proviene de la definición formal del tipo. Si un tipo se considera como el conjunto de todos los valores que puede tomar, un tipo unión es simplemente la unión matemática de sus tipos constituyentes, ya que puede tomar cualquier valor que cualquiera de sus campos pueda tomar. Además, debido a que una unión matemática descarta duplicados, si más de un campo de la unión puede tomar un único valor común, es imposible determinar, solo con el valor, qué campo se modificó por última vez.

Sin embargo, una función útil de las uniones en la programación es la de mapear elementos de datos más pequeños a otros más grandes para facilitar su manipulación. Por ejemplo, una estructura de datos compuesta por 4 bytes y un entero de 32 bits puede formar una unión con un entero sin signo de 64 bits, lo que permite un acceso más sencillo para comparaciones, etc.

Uniones en varios lenguajes de programación

ALGOL 68

ALGOL 68 tiene uniones etiquetadas y utiliza una cláusula case para distinguir y extraer el tipo constituyente en tiempo de ejecución. Una unión que contiene otra unión se trata como el conjunto de todas sus posibilidades constituyentes, y si el contexto lo requiere, una unión se convierte automáticamente en la unión más amplia. Una unión puede no contener explícitamente ningún valor, lo cual se puede distinguir en tiempo de ejecución. Un ejemplo es:

nodo de modo = unión ( real , int , string , void ); nodo n := "abc"; caso n en ( real r): imprimir(("real:", r)), ( int i): print(("int:", i)), ( cadena s): imprimir(("cadena:", s)), ( void ): print(("void:", "VACÍO")), out print(("?:", n)) esac

La sintaxis del tipo de unión de C/C++ y la noción de conversiones de tipo se derivaron de ALGOL 68, aunque en una forma sin etiquetar. [ 1 ]

C/C++

En C y C++ , las uniones sin etiquetar se expresan casi exactamente como estructuras ( structs ), excepto que cada miembro de datos se encuentra en la misma dirección de memoria. Los miembros de datos, como en las estructuras, no tienen por qué ser valores primitivos y, de hecho, pueden ser estructuras o incluso otras uniones. C++ (desde C++11 ) también permite que un miembro de datos sea de cualquier tipo que tenga un constructor/destructor completo y/o un constructor de copia, o un operador de asignación de copia no trivial. Por ejemplo, es posible tener la cadena estándar de C++ como miembro de una unión.

El uso principal de una unión es permitir el acceso a una ubicación común mediante diferentes tipos de datos, por ejemplo, acceso a entrada/salida de hardware, compartición de campos de bits y palabras, o reconfiguración de tipos . Las uniones también pueden proporcionar polimorfismo de bajo nivel. Sin embargo, no se realiza ninguna comprobación de tipos, por lo que es responsabilidad del programador asegurarse de que se acceda a los campos correctos en diferentes contextos. El campo relevante de una variable de unión suele estar determinado por el estado de otras variables, posiblemente en una estructura contenedora.

Una práctica común en la programación en C utiliza uniones para realizar lo que C++ denomina una operación de asignación reinterpret_cast, asignando un valor a un campo de la unión y leyendo de otro, como se hace en el código que depende de la representación directa de los valores. Un ejemplo práctico es el método para calcular raíces cuadradas utilizando la representación IEEE . Sin embargo, este no es un uso seguro de las uniones en general.

Los especificadores de estructura y unión tienen la misma forma. [...] El tamaño de una unión es suficiente para contener el mayor de sus miembros. El valor de como máximo uno de los miembros puede almacenarse en un objeto de unión en cualquier momento. Un puntero a un objeto de unión, debidamente convertido, apunta a cada uno de sus miembros (o, si un miembro es un campo de bits, a la unidad en la que reside), y viceversa.

ANSI/ISO 9899:1990 (la norma ANSI C) Sección 6.5.2.1

Unión anónima

En C++, C11 y como extensión no estándar en muchos compiladores, las uniones también pueden ser anónimas. Sus miembros de datos no necesitan ser referenciados, sino que se acceden directamente. Presentan algunas restricciones en comparación con las uniones tradicionales: en C11, deben ser miembros de otra estructura o unión, [ 2 ] y en C++, no pueden tener métodos ni especificadores de acceso.

El simple hecho de omitir la parte del nombre de la clase en la sintaxis no convierte una unión en una unión anónima. Para que una unión se considere anónima, la declaración no debe declarar ningún objeto. Ejemplo:

unión { float f ; uint32_t d ; // Se asume que float tiene 32 bits de ancho };f = 3.14f ; printf ( " Representación hexadecimal de 3.14f: %x \n " , u.d ) ;

Las uniones anónimas también son útiles en structlas definiciones de C para proporcionar una sensación de espacio de nombres. [ 3 ]

Unión transparente

En compiladores como GCC, Clang e IBM XL C para AIX, transparent_unionexiste un atributo disponible para los tipos de unión. Los tipos contenidos en la unión se pueden convertir de forma transparente al tipo de unión en sí mismo en una llamada a función, siempre que todos los tipos tengan el mismo tamaño. Está pensado principalmente para funciones con múltiples interfaces de parámetros, un uso necesario debido a las primeras extensiones de Unix y la posterior reestandarización. [ 4 ]

COBOL

En COBOL , los elementos de datos de unión se definen de dos maneras. La primera utiliza la palabra clave RENAMES (nivel 66), que asigna un segundo elemento de datos alfanumérico a la misma ubicación de memoria que un elemento de datos anterior. En el código de ejemplo que se muestra a continuación, el elemento de datos PERSON-REC se define como un grupo que contiene otro grupo y un elemento de datos numérico. PERSON-DATA se define como un elemento de datos alfanumérico que renombra a PERSON-REC , tratando los bytes de datos que contiene como datos de caracteres.

 01 PERSON-REC . 05 PERSON-NAME . 10 PERSON-NAME-LAST PIC X(12) . 10 PERSON-NAME-FIRST PIC X(16) . 10 PERSON-NAME-MID PIC X . 05 PERSON-ID PIC 9(9) PACKED-DECIMAL .  01 PERSON-DATA RENOMBRA PERSON-REC .

La segunda forma de definir un tipo de unión es mediante la palabra clave REDEFINES . En el código de ejemplo que se muestra a continuación, el elemento de datos VERS-NUM se define como un entero binario de 2 bytes que contiene un número de versión. Un segundo elemento de datos, VERS-BYTES , se define como una variable alfanumérica de dos caracteres. Dado que el segundo elemento se redefine sobre el primero, ambos comparten la misma dirección de memoria y, por lo tanto, los mismos bytes de datos subyacentes. El primer elemento interpreta los dos bytes de datos como un valor binario, mientras que el segundo los interpreta como valores de caracteres.

 01 VERS-INFO . 05 VERS-NUM PIC S9(4) COMP . 05 VERS-BYTES PIC X(2) REDEFINES VERS-NUM

Pascal

En Pascal , existen dos maneras de crear uniones. Una es la forma estándar mediante un registro de variante. La segunda es un método no estándar que consiste en declarar una variable como absoluta, es decir, ubicarla en la misma dirección de memoria que otra variable o en una dirección absoluta. Si bien todos los compiladores de Pascal admiten registros de variante, solo algunos admiten variables absolutas.

Para los fines de este ejemplo, los siguientes son todos tipos de números enteros: un byte consta de 8 bits, una palabra de 16 bits y un número entero de 32 bits.

El siguiente ejemplo muestra la forma absoluta no estándar:

var A : Entero ; B : matriz [ 1 .. 4 ] de Byte absoluto A ; C : Entero absoluto 0 ;

En el primer ejemplo, cada uno de los elementos del arreglo B se corresponde con uno de los bytes específicos de la variable A. En el segundo ejemplo, la variable C se asigna a la dirección exacta de la máquina 0.

En el siguiente ejemplo, un registro tiene variantes, algunas de las cuales comparten la misma ubicación que otras:

tipo Forma = ( Círculo , Cuadrado , Triángulo ) ; Dimensiones = registro caso Figura : Forma de Círculo : ( Diámetro : real ) ; Cuadrado : ( Ancho : real ) ; Triángulo : ( Lado : real ; Ángulo1 , Ángulo2 : 0 .. 360 ) fin ;

PL/I

En PL/I, el término original para una unión era celda , [ 5 ] que todavía es aceptado como sinónimo de unión por varios compiladores. La declaración de unión es similar a la definición de estructura, donde los elementos del mismo nivel dentro de la declaración de unión ocupan el mismo espacio de almacenamiento. Los elementos de la unión pueden ser de cualquier tipo de dato, incluidas estructuras y matrices. [ 6 ] : pp192–193 Aquí vers_num y vers_bytes ocupan las mismas ubicaciones de almacenamiento.

1 vers_info unión , 5 vers_num binario fijo , 5 vers_bytes pic '(2)A';

Una alternativa a la declaración de unión es el atributo DEFINED, que permite declaraciones alternativas de almacenamiento; sin embargo, los tipos de datos de las variables base y definidas deben coincidir. [ 6 ] : pp.289–293

Óxido

Rust implementa uniones etiquetadas y no etiquetadas. En Rust, las uniones etiquetadas se implementan usando la enumpalabra clave. A diferencia de los tipos enumerados en la mayoría de los demás lenguajes, las variantes de enumeración en Rust pueden contener datos adicionales en forma de tupla o estructura, lo que las convierte en uniones etiquetadas en lugar de simples tipos enumerados. [ 7 ]

Rust también admite uniones sin etiquetar usando la unionpalabra clave. La disposición en memoria de las uniones en Rust no está definida por defecto, [ 8 ] pero una unión con el #[repr(C)]atributo se dispondrá en memoria exactamente igual que la unión equivalente en C. [ 9 ] La lectura de los campos de una unión solo se puede realizar dentro de una unsafefunción o bloque, ya que el compilador no puede garantizar que los datos en la unión sean válidos para el tipo del campo; si este no es el caso, dará como resultado un comportamiento indefinido . [ 10 ]

Sintaxis y ejemplo

do

En C, la sintaxis es:

unión < nombre > { < tipo de dato > < nombre de la primera variable > ; < tipo de dato > < nombre de la segunda variable > ; . . . < tipo de dato > < nombre de la enésima variable > ; } < nombre de la variable de unión > ;

Una estructura también puede ser miembro de un sindicato, como muestra el siguiente ejemplo:

unión { struct { int a ; float b ; char c ; } svar ; int d ; } uvar ;

Este ejemplo define una variable uvarcomo una unión que contiene dos miembros, una estructura llamada svar(que a su vez contiene tres miembros) y una variable entera llamada d.

Las uniones pueden producirse dentro de estructuras y matrices, y viceversa:

#define N 10000struct { int flags ; char * name ; int utype ; union { int ival ; float fval ; char * sval ; } u ; } symtab [ N ];

El número ivalse denomina symtab[i].u.ivaly la cadena sval, symtab[i].u.sval.

C++

C++ admite uniones como C, pero son difíciles de usar de forma segura en C++. Para los dos propósitos principales de las uniones, el C++ idiomático tiene alternativas: [ 11 ] [ 12 ]

  1. La tipificación mediante uniones, como mapear un valor de punto flotante a su representación binaria como un entero, se puede realizar con .union{uint32_tx;floaty;}reinterpret_cast
  2. Para los tipos que no son POD , las uniones requieren construcción y destrucción explícitas, lo que las hace engorrosas para usar de forma segura. std::variant, introducido en C++17 , actúa como una unión etiquetada , evitando esta complicación.
#include <cmath> #include <cstdint> #include <stdexcept> #include <variant>uint32_t encode_float_as_binary ( float f ) noexcept { return reinterpret_cast < uint32_t &> ( f ); }bool is_integral ( std :: variant < int , float >& v ) { if ( int * _ = std :: get_if <int> ( & v )) { return true ; } else if ( float * f = std :: get_if <float> ( & v )) { return std :: modff ( * f , nullptr ) == 0 ; } throw std :: invalid_argument ( " ¡ Entrada de variante no válida!" ) ; }

DO#

Hasta la versión 15 de C#, no existían tipos de unión en C#. La forma más cercana de emularlos era mediante registros y coincidencia de patrones .

espacio de nombres Wikipedia.Ejemplos ;usando el sistema ;Registro abstracto Forma ; registro Círculo ( doble Radio ) : Forma ; registro Rectángulo ( doble Ancho , doble Alto ) : Forma ;public class Example { public static double Area ( Shape s ) => s switch { Circle c => Math . PI * c . Radius * c . Radius , Rectangle r => r . Width * r . Height , _ => throw new NotSupportedException () }; }

También podrían simularse con precisión mediante un diseño de bajo nivel. El siguiente ejemplo muestra una unión de este tipo, donde los campos ocupan la misma memoria, pero no es segura en cuanto a tipos. Se recomienda su uso para la interoperabilidad, la serialización y la manipulación no segura.

usando System.Runtime.InteropServices ;// más cercano a C union IntFloatUnion { int i; float f; }; [StructLayout(LayoutKind.Explicit)] struct IntFloatUnion { [FieldOffset(0)] public int IntValue ;[FieldOffset(0)] public float FloatValue ; }

En C# 15, se introdujeron las uniones en el lenguaje, como se puede demostrar a continuación: [ 13 ]

clase de registro público Coche ( cadena Modelo ); clase de registro público Bicicleta ( cadena Modelo ); clase de registro público Autobús ( cadena Modelo );Unión pública Vehículo ( Automóvil , Bicicleta , Autobús );Vehículo coche = nuevo Coche ( "Tesla Model 3" ); Consola . WriteLine ( coche . Value ); // Coche { Modelo = Tesla Model 3 }Vehículo bicicleta = nueva Bicicleta ( "Giant Escape 3" ); Consola . WriteLine ( bicicleta . Value ); // Bicicleta { Modelo = Giant Escape 3 }Vehículo autobús = nuevo Autobús ( "Volvo 9700" ); Consola . WriteLine ( autobús . Value ); // Autobús { Modelo = Volvo 9700 }Vehículo v = /* algún vehículo aquí */ ; string modelo = v switch { Coche c => c . Modelo , Bicicleta bk => b . Modelo , Autobús bs => bs . Modelo , };

El defaultvalor de una unión es null, pero si todos los tipos en la unión no son anulables , entonces una switchexpresión no necesita comprobar null.

Java

En Java no existen tipos de unión, aunque se pueden emular de alguna manera utilizando interfaces .

// "Shape" puede considerarse como una unión de "Circle", "Rectangle", // y otros tipos que implementan "Shape" . // Podemos restringir explícitamente lo que está en la unión usando una interfaz sellada: sealed interface Shape permite Circle , Rectangle {}Registro Círculo ( doble radio ) implementa Forma {}Registro Rectángulo ( doble ancho , doble alto ) implementa Forma {}clase Ejemplo { double área ( Forma s ) { return switch ( s ) { case Círculo ( double r ) -> Math . PI * r * r ; case Rectángulo ( double w , double h ) -> w * h ; }; } }

Una catchcláusula puede declarar una unión de múltiples tipos de excepción. Esta sintaxis se asemeja a la sintaxis de tipos de unión en otros lenguajes, pero en realidad no introduce un nuevo tipo. El tipo efectivo del parámetro de excepción es el supertipo común más específico de las alternativas. La cláusula catch solo captura excepciones de los tipos declarados, no de otras subclases del supertipo común. [ 14 ]

clase FooException extiende RuntimeException { String nombre () { return "Foo" ; } }clase BarException extiende RuntimeException { String nombre () { return "Bar" ; } }void main () { try { throw Math . random () < 0.5 ? new FooException () : new BarException (); } catch ( FooException | BarException e ) { // e tiene el tipo RuntimeException, el supertipo común de FooException y BarException System . out . println ( e ); e . printStackTrace (); // System.out.println(e.name()); // Debido a que no existe el método name() en RuntimeException, esto no compila } }

PHP

Los tipos de unión se introdujeron en PHP 8.0. [ 15 ] Los valores son implícitamente "etiquetados" con un tipo por el lenguaje y pueden recuperarse mediante "gettype()".

clase Ejemplo { int privado | float $foo ;public function squareAndAdd ( float | int $bar ) : int | float { return $bar ** 2 + $this -> foo ; } }

Pitón

El soporte para tipado se introdujo en Python 3.5. [ 16 ] La nueva sintaxis para tipos de unión se introdujo en Python 3.10. [ 17 ]

desde la escritura importar UniónClase Ejemplo : foo : int = 0# estilo antiguo: def square_and_add ( self , bar : Union [ int , float ]) -> Union [ int , float ]: return bar ** 2 + self . foo# nuevo estilo: def square_and_add ( self , bar : int | float ) -> int | float : return bar ** 2 + self . foo

Mecanografiado

TypeScript admite tipos de unión. [ 18 ] El lenguaje etiqueta implícitamente los valores con un tipo y puede recuperarlos mediante una typeofllamada para valores primitivos y una instanceofcomparación para tipos de datos complejos. Los tipos con usos superpuestos (por ejemplo, un método slice existe tanto en cadenas como en matrices, el operador plus funciona tanto en cadenas como en números) no necesitan una restricción adicional para usar estas características.

función sucesor ( n : número | bigint ) : número | bigint { // los tipos que admiten las mismas operaciones no necesitan estrechamiento return ++ n ; }function dependsOnParameter ( v : string | string [] | number ) { // Los tipos distintos necesitan ser restringidos if ( v instanceof Array ) { // hacer algo } else if ( typeof ( v ) === "string" ) { // hacer otra cosa } else { // Tiene que ser un número } }

Óxido

Las uniones etiquetadas en Rust usan la enumpalabra clave y pueden contener variantes de tuplas y estructuras:

enum Foo { Bar ( i32 ), Baz { x : String , y : i32 }, }

Las uniones sin etiquetar en Rust utilizan la unionpalabra clave:

unión Foo { bar : i32 , baz : bool , }

La lectura de los campos de una unión sin etiquetar produce un comportamiento indefinido si los datos de la unión no son válidos como el tipo del campo y, por lo tanto, requiere un unsafebloque:

let x = Foo { bar : 10 }; let y = unsafe { x . bar }; // Esto establecerá y en 10 y no produce un comportamiento indefinido. let z = unsafe { x . baz }; // Esto produce un comportamiento indefinido, ya que el valor almacenado en x no es un booleano válido.

Referencias

  1. Ritchie, Dennis M. (marzo de 1993). "El desarrollo del lenguaje C" . ACM SIGPLAN Notices . 28 (3): 201– 208. doi : 10.1145/155360.155580 . El esquema de composición de tipos adoptado por C debe una deuda considerable a Algol 68, aunque quizás no surgió en una forma que los seguidores de Algol aprobarían. La noción central que capturé de Algol fue una estructura de tipos basada en tipos atómicos (incluidas las estructuras), compuesta en arreglos, punteros (referencias) y funciones (procedimientos). El concepto de uniones y conversiones de Algol 68 también tuvo una influencia que apareció más tarde.
  2. "6.63 Estructura sin nombre y campos de unión" . Consultado el 29 de diciembre de 2016 .
  3. Siebenmann., Chris. "CUnionsForNamespaces" . utcc.utoronto.ca .
  4. "Atributos de tipo comunes: transparent_union" . Uso de la colección de compiladores GNU (GCC) .
  5. IBM Corporation (marzo de 1968). Especificaciones del lenguaje PL/I de IBM System/360 (PDF) . pág. 52. Consultado el 22 de enero de 2018 . 
  6. 1 2 IBM Corporation (dic. 2017). Enterprise PL/I para z/OS PL/I para AIX IBM Developer para z Systems PL/I para windows Language Reference (PDF) . Recuperado el 22 de enero de 2018 .
  7. "Cómo Rust implementa las uniones etiquetadas - Pat Shaughnessy" . patshaughnessy.net . Consultado el 25 de abril de 2023 .
  8. "Tipos de unión - La referencia de Rust" . doc.rust-lang.org . Consultado el 25 de abril de 2023 .
  9. "Diseño de tipos - La referencia de Rust" . doc.rust-lang.org . Consultado el 25 de abril de 2023 .
  10. "Uniones - La referencia de Rust" . doc.rust-lang.org . Consultado el 25 de abril de 2023 .
  11. Bancila, Marius (2017). "Using std::variant as a type-safe union". Modern C++ Programming Cookbook . O'Reilly Media . ISBN 9781786465184.
  12. "std::variant" . cppreference.com . Archivado del original el 11 de octubre de 2025. Consultado el 16 de diciembre de 2025 .
  13. Bill Wagner (2 de abril de 2026). "Explora los tipos de unión en C# 15" . devblogs.microsoft.com . Microsoft.
  14. Gosling, James ; Joy, Bill ; Steele, Guy ; Bracha, Gilad ; Buckley, Alex; Smith, Daniel; Bierman, Gavin. "Especificación del lenguaje Java. Sección 14.20: La instrucción try" . Consultado el 22 de abril de 2026 .
  15. Karunaratne, Ayesh. "PHP 8.0: Tipos de unión" . PHP.Watch . Consultado el 30 de noviembre de 2020 .
  16. "typing — Soporte para sugerencias de tipo — Documentación de Python 3.9.7" . docs.python.org . Consultado el 8 de septiembre de 2021 .
  17. "PEP 604 -- Permitir escribir tipos de unión como X | Y" . Python.org . Consultado el 8 de septiembre de 2021 .
  18. "Manual - Uniones y tipos de intersección" . www.typescriptlang.org . Consultado el 30 de noviembre de 2020 .
  • Kernighan, Brian W.; Ritchie, Dennis M. (1978). El lenguaje de programación C (1.ª  ed.). Prentice Hall. pág . 138. ISBN  978-0131101630. Consultado el 23 de enero de 2018 .
  • boost::variant , una alternativa con seguridad de tipos a las uniones de C++
  • MSDN: Clases, estructuras y uniones , para ejemplos y sintaxis.
  • diferencias , diferencias entre unión y estructura
  • Diferencia entre struct y union en C++