Namespaces
Variants

Type

From cppreference.net

(Véase también tipos aritméticos para los detalles sobre la mayoría de los tipos incorporados y la lista de utilidades relacionadas con tipos que proporciona la biblioteca de C.)

Objetos , funciones , y expresiones tienen una propiedad llamada tipo , que determina la interpretación del valor binario almacenado en un objeto o evaluado por la expresión.

Contenidos

Clasificación de tipos

El sistema de tipos de C consta de los siguientes tipos:

  • el tipo void
  • tipos básicos
  • el tipo char
  • tipos enteros con signo
  • estándar: signed char , short , int , long , long long (desde C99)
  • de precisión de bits: _BitInt ( N ) donde N es una expresión constante entera que especifica el número de bits utilizados para representar el tipo, incluyendo el bit de signo. Cada valor de N designa un tipo distinto.
(desde C23)
  • extendido: definido por la implementación, por ejemplo __int128
(desde C99)
  • tipos de enteros sin signo
  • estándar: _Bool , (since C99) unsigned char , unsigned short , unsigned int , unsigned long , unsigned long long (since C99)
  • de precisión de bits: unsigned _BitInt ( N ) donde N es una expresión constante entera que especifica el número de bits utilizados para representar el tipo. Cada valor de N designa un tipo distinto. Esta categoría incluye el tipo unsigned _BitInt ( 1 ) que no tiene un tipo entero con signo de precisión de bits correspondiente.
(desde C23)
  • extendido: definido por la implementación, ej. __uint128
(desde C99)
  • tipos de punto flotante
  • tipos de punto flotante reales: float , double , long double
  • tipos decimales de coma flotante reales: _Decimal32 , _Decimal64 , _Decimal128
(desde C23)
  • tipos complejos: float _Complex , double _Complex , long double _Complex
  • tipos imaginarios: float _Imaginary , double _Imaginary , long double _Imaginary
(desde C99)
  • tipos derivados
(desde C11)

Para cada tipo listado anteriormente, pueden existir varias versiones calificadas de su tipo, correspondientes a las combinaciones de uno, dos o los tres de los const , volatile , y restrict calificadores (cuando esté permitido por la semántica del calificador).

Grupos de tipos

  • tipos de objeto : todos los tipos que no son tipos de función
  • tipos de carácter : char , signed char , unsigned char
  • tipos enteros : char , tipos enteros con signo, tipos enteros sin signo, tipos enumerados
  • tipos reales : tipos enteros y tipos de coma flotante real
  • tipos aritméticos : tipos enteros y tipos de coma flotante
  • tipos escalares : tipos aritméticos, tipos puntero , y nullptr_t (desde C23)
  • tipos agregados : tipos array y tipos estructura
  • tipos de declarador derivados : tipos array, tipos función y tipos puntero

Construir un tipo de objeto completo de manera que el número de bytes en su representación de objeto no sea representable en el tipo size_t (es decir, el tipo resultante del operador sizeof ) , incluyendo la formación de dicho tipo VLA en tiempo de ejecución, (desde C99) es comportamiento indefinido.

Tipos compatibles

En un programa C, las declaraciones que se refieren al mismo objeto o función en diferentes unidades de traducción no tienen que usar el mismo tipo. Solo tienen que usar tipos suficientemente similares, formalmente conocidos como tipos compatibles . Lo mismo se aplica a las llamadas a funciones y accesos a lvalues; los tipos de argumentos deben ser compatibles con los tipos de parámetros y el tipo de expresión lvalue debe ser compatible con el tipo de objeto al que se accede.

Los tipos T y U son compatibles, si

  • son del mismo tipo (mismo nombre o alias introducidos por un typedef )
  • son versiones idénticamente calificadas cvr de tipos no calificados compatibles
  • son tipos puntero y apuntan a tipos compatibles
  • son tipos array, y
  • sus tipos de elemento son compatibles, y
  • si ambos tienen tamaño constante, ese tamaño es el mismo. Nota: los arrays de límite desconocido son compatibles con cualquier array de tipo de elemento compatible. VLA es compatible con cualquier array de tipo de elemento compatible. (desde C99)
  • ambos son tipos de estructura/unión/enumeración, y
  • (C99) si uno se declara con una etiqueta, el otro también debe declararse con la misma etiqueta.
  • si ambos son tipos completos, sus miembros deben corresponder exactamente en número, declararse con tipos compatibles y tener nombres coincidentes.
  • adicionalmente, si son enumeraciones, los miembros correspondientes también deben tener los mismos valores.
  • adicionalmente, si son estructuras o uniones,
  • Los miembros correspondientes deben declararse en el mismo orden (solo estructuras)
  • Los campos de bits correspondientes deben tener los mismos anchos.
  • uno es un tipo enumerado y el otro es el tipo subyacente de esa enumeración
  • son tipos de función, y
  • sus tipos de retorno son compatibles
  • ambos utilizan listas de parámetros, el número de parámetros (incluyendo el uso de la elipsis) es el mismo, y el parámetro correspondiente, después de aplicar ajustes de tipo array-a-puntero y función-a-puntero y después de eliminar calificadores de nivel superior, tienen tipos compatibles
  • una es una definición de estilo antiguo (sin parámetros), la otra tiene una lista de parámetros, la lista de parámetros no utiliza puntos suspensivos y cada parámetro es compatible (después del ajuste de tipo de parámetro de función) con el parámetro de estilo antiguo correspondiente después de las promociones de argumentos predeterminadas
  • una es una declaración de estilo antiguo (sin parámetros), la otra tiene una lista de parámetros, la lista de parámetros no utiliza puntos suspensivos, y todos los parámetros (después del ajuste de tipo de parámetro de función) no se ven afectados por las promociones de argumentos predeterminadas
(hasta C23)

El tipo char no es compatible con signed char y no es compatible con unsigned char .

Si dos declaraciones se refieren al mismo objeto o función y no utilizan tipos compatibles, el comportamiento del programa es indefinido.

// Unidad de Traducción 1
struct S { int a; };
extern struct S *x; // compatible con x de TU2, pero no con x de TU3
// Unidad de Traducción 2
struct S;
extern struct S *x; // compatible con ambas x
// Unidad de Traducción 3
struct S { float a; };
extern struct S *x; // compatible con x de TU2, pero no con x de TU1
// el comportamiento no está definido
// Unidad de Traducción 1
#include <stdio.h>
struct s { int i; }; // compatible con s de TU3, pero no con s de TU2
extern struct s x = {0}; // compatible con x de TU3
extern void f(void); // compatible con f de TU2
int main()
{
    f();
    return x.i;
}
// Unidad de Traducción 2
struct s { float f; }; // compatible con s de TU4, pero no con s de TU1
extern struct s y = {3.14}; // compatible con y de TU4
void f() // compatible con f de TU1
{
    return;
}
// Unidad de Traducción 3
struct s { int i; }; // compatible con s de TU1, pero no con s de TU2
extern struct s x; // compatible con x de TU1
// Unidad de Traducción 4
struct s { float f; }; // compatible con s de TU2, pero no con s de TU1
extern struct s y; // compatible con y de TU2
// el comportamiento está bien definido: solo las declaraciones múltiples
// de objetos y funciones deben tener tipos compatibles, no los tipos en sí mismos

Nota: C++ no tiene concepto de tipos compatibles. Un programa en C que declara dos tipos que son compatibles pero no idénticos en diferentes unidades de traducción no es un programa válido en C++.

Tipos compuestos

Un tipo compuesto puede construirse a partir de dos tipos que son compatibles; es un tipo que es compatible con ambos tipos y satisface las siguientes condiciones:

  • Si ambos tipos son tipos de arreglo, se aplican las siguientes reglas:
  • Si un tipo es un arreglo de tamaño constante conocido, el tipo compuesto es un arreglo de ese tamaño.
  • De lo contrario, si un tipo es un VLA cuyo tamaño se especifica mediante una expresión que no se evalúa, un programa que requiera el tipo compuesto de ambos tipos tiene comportamiento indefinido.
  • De lo contrario, si un tipo es un VLA con tamaño especificado, el tipo compuesto es un VLA de ese tamaño.
  • De lo contrario, si un tipo es un VLA de tamaño no especificado, el tipo compuesto es un VLA de tamaño no especificado.
(desde C99)
  • De lo contrario, ambos tipos son arrays de tamaño desconocido y el tipo compuesto es un array de tamaño desconocido.
El tipo de elemento del tipo compuesto es el tipo compuesto de los dos tipos de elemento.
  • Si solo un tipo es un tipo de función con una lista de tipos de parámetros (un prototipo de función), el tipo compuesto es un prototipo de función con la lista de tipos de parámetros.
(hasta C23)
  • Si ambos tipos son tipos de función con listas de tipos de parámetros, el tipo de cada parámetro en la lista de tipos de parámetros compuesta es el tipo compuesto de los parámetros correspondientes.

Estas reglas se aplican recursivamente a los tipos de los cuales se derivan los dos tipos.

// Dadas las siguientes dos declaraciones de ámbito de archivo:
int f(int (*)(), double (*)[3]);
int f(int (*)(char *), double (*)[]); // C23: Error: tipos conflictivos para 'f'
// El tipo compuesto resultante para la función es:
int f(int (*)(char *), double (*)[3]);

Para un identificador con enlace interno o externo declarado en un ámbito en el que una declaración previa de ese identificador es visible, si la declaración previa especifica enlace interno o externo, el tipo del identificador en la declaración posterior se convierte en el tipo compuesto.

Tipos incompletos

Un tipo incompleto es un tipo de objeto que carece de información suficiente para determinar el tamaño de los objetos de ese tipo. Un tipo incompleto puede completarse en algún punto de la unidad de traducción.

Los siguientes tipos están incompletos:

  • el tipo void . Este tipo no puede ser completado.
  • tipo array de tamaño desconocido. Puede ser completado mediante una declaración posterior que especifique el tamaño.
extern char a[]; // el tipo de a es incompleto (esto suele aparecer en un encabezado)
char a[10];      // el tipo de a ahora está completo (esto suele aparecer en un archivo fuente)
  • tipo de estructura o unión de contenido desconconocido. Puede ser completado mediante una declaración de la misma estructura o unión que defina su contenido posteriormente en el mismo ámbito.
struct node
{
    struct node* next; // struct node está incompleto en este punto
}; // struct node está completo en este punto

Nombres de tipos

Un tipo puede tener que ser nombrado en un contexto diferente al de la declaración . En estas situaciones, se utiliza el nombre de tipo , que es, gramaticalmente, exactamente igual que una lista de especificadores de tipo y calificadores de tipo , seguida por el declarador (ver declaraciones ) como se usaría para declarar un único objeto o función de este tipo, excepto que se omite el identificador:

int n; // declaración de un int
sizeof(int); // uso de nombre de tipo
int *a[3]; // declaración de un array de 3 punteros a int
sizeof(int *[3]); // uso de nombre de tipo
int (*p)[3]; // declaración de un puntero a array de 3 int
sizeof(int (*)[3]); // uso de nombre de tipo
int (*a)[*] // declaración de puntero a VLA (en un parámetro de función)
sizeof(int (*)[*]) // uso de nombre de tipo (en un parámetro de función)
int *f(void); // declaración de función
sizeof(int *(void)); // uso de nombre de tipo
int (*p)(void); // declaración de puntero a función
sizeof(int (*)(void)); // uso de nombre de tipo
int (*const a[])(unsigned int, ...) = {0}; // array de punteros a funciones
sizeof(int (*const [])(unsigned int, ...)); // uso de nombre de tipo

Excepto que los paréntesis redundantes alrededor del identificador son significativos en un nombre de tipo y representan "función sin especificación de parámetros":

int (n); // declara n de tipo int
sizeof(int ()); // utiliza el tipo "función que retorna int"

Los nombres de tipos se utilizan en las siguientes situaciones:

(desde C99)
(desde C11)


Un nombre de tipo puede introducir un nuevo tipo:

void* p = (void*)(struct X { int i; } *)0;
// el nombre de tipo "struct X {int i;}*" usado en la expresión de conversión
// introduce el nuevo tipo "struct X"
struct X x = {1}; // struct X ahora está en el ámbito

Referencias

  • Estándar C23 (ISO/IEC 9899:2024):
  • 6.2.5 Tipos (p: TBD)
  • 6.2.6 Representaciones de tipos (p: TBD)
  • 6.2.7 Tipo compatible y tipo compuesto (p: TBD)
  • Estándar C17 (ISO/IEC 9899:2018):
  • 6.2.5 Tipos (p: 31-33)
  • 6.2.6 Representaciones de tipos (p: 31-35)
  • 6.2.7 Tipo compatible y tipo compuesto (p: 35-36)
  • Estándar C11 (ISO/IEC 9899:2011):
  • 6.2.5 Tipos (p: 39-43)
  • 6.2.6 Representaciones de tipos (p: 44-46)
  • 6.2.7 Tipo compatible y tipo compuesto (p: 47-48)
  • Estándar C99 (ISO/IEC 9899:1999):
  • 6.2.5 Tipos (p: 33-37)
  • 6.2.6 Representaciones de tipos (p: 37-40)
  • 6.2.7 Tipo compatible y tipo compuesto (p: 40-41)
  • Estándar C89/C90 (ISO/IEC 9899:1990):
  • 3.1.2.5 Tipos
  • 3.1.2.6 Tipo compatible y tipo compuesto

Véase también