El sistema de tipo C es una implementación del concepto de tipo de datos en el lenguaje de programación C. El propio lenguaje proporciona tipos aritméticos básicos, así como una sintaxis para crear matrices y tipos compuestos. Algunos archivos de encabezado de la biblioteca estándar de C contienen definiciones de tipo con propiedades adicionales [1] [2] .
El lenguaje C proporciona muchos tipos básicos. La mayoría de ellos se forman utilizando uno de los cuatro especificadores de tipo aritmético, ( char, y ) int, y especificadores opcionales ( , y ). Aunque el estándar especifica un rango calculado a partir de la fórmula de −(2 n−1 −1) a 2 n−1 −1 , todos los compiladores conocidos ( gcc , clang y el compilador de Microsoft ) permiten el rango de −(2 n−1 ) a 2 n −1 −1 , donde n es el ancho de bit del tipo. floatdoublesignedunsignedshortlong
La siguiente tabla asume que 1 byte = 8 bits.
En la gran mayoría de las plataformas modernas, esto es cierto, pero es posible que 1 byte sea igual a 16 bits o algún otro número, generalmente una potencia de dos.
Tipo de | Explicación | Especificador de formato |
---|---|---|
char | Entero, el tipo direccionable más pequeño posible. Puede contener el juego de caracteres base. Puede ser firmado o no firmado, dependiendo de la implementación. Contiene CHAR_BIT(generalmente 8) bits. [3] | %c |
signed char | Mismo tamaño que char, pero garantizado para ser firmado. Puede tomar valores de al menos el rango [−127, +127][3] , generalmente en implementaciones [4][−128, +127] | %c (también %do %hhi( %hhx, %hho) para salida numérica) |
unsigned char | Mismo tamaño que char, pero se garantiza que no estará firmado. Rango: [3] . Normalmente,[0, 2CHAR_BIT − 1][0, 255] | %c (o %hhupara salida numérica) |
short short int signed short signed short int |
El tipo de un entero corto con signo. Puede contener números de al menos el rango [−32767, +32767][3] , típicamente en implementaciones [4] . Por lo tanto, es al menos 16 bits (2 bytes).[−32768, +32767] | %hi |
unsigned short unsigned short int |
Igual que shortpero sin firmar. Rango:[0, +65535] | %hu |
int signed signed int |
El tipo básico de un entero con signo. Puede contener números de al menos el rango [−32767, +32767][3] . Por lo tanto, es al menos 16 bits (2 bytes). Normalmente, 4 bytes de tamaño y rango en compiladores modernos para plataformas de 32 bits y superiores [−2 147 483 648, +2 147 483 647], pero normalmente 2 bytes de rango en plataformas de 16 y 8 bits [−32768, +32767], lo que a menudo causa confusión y conduce a incompatibilidades Código mal escrito | %io%d |
unsigned unsigned int |
Igual que intpero sin firmar. Rango:[0, +4 294 967 295] | %u |
long long int signed long signed long int |
Tipo entero largo con signo. Puede contener números al menos en el rango [−2 147 483 647, +2 147 483 647]. [3] [4] [5] Así que son al menos 32 bits (4 bytes). | %lio%ld |
unsigned long unsigned long int |
Igual que longpero sin firmar. Rango:[0, +4 294 967 295] | %lu |
long long long long int signed long long signed long long int |
El tipo de entero con signo largo largo ( doble largo ). Puede contener números al menos en el rango [−9 223 372 036 854 775 808, +9 223 372 036 854 775 807]. [3] [4] Así que son al menos 64 bits. Aprobado en la norma C99 . |
%llio%lld |
unsigned long long unsigned long long int |
Similar a long longpero sin firmar. Rango : [0, 18 446 744 073 709 551 615]. | %llu |
float | Un tipo de número de punto flotante real, comúnmente denominado tipo de número de punto flotante de precisión simple. Las propiedades detalladas no se especifican en el estándar (excepto los límites mínimos); sin embargo, en la mayoría de los sistemas es el formato binario de punto flotante de precisión simple IEEE 754 . Este formato es necesario para la aritmética de coma flotante "IEC 60559" opcional del Anexo F. | %f (convertido automáticamente a doublepara printf()) |
double | Un tipo de punto flotante real, comúnmente denominado tipo de número de punto flotante de precisión doble. En la mayoría de los sistemas, cumple con el formato de punto flotante binario de doble precisión IEEE 754 . | %f( %F)
( %lf( %lF) para scanf()) |
long double | Un tipo de número de punto flotante real, generalmente asignado al formato de número de punto flotante de alta precisión A diferencia de y , puede ser de punto flotante de 80 bits, "doble-doble" no IEEE o "punto flotante binario de precisión cuádruple IEEE 754". Si no se proporciona un formato más preciso, es equivalente a . Vea el artículo sobre el doble largo para más detalles.floatdoubledouble | %Lf %LF %Lg %LG %Le %LE[6] |
Tampoco se mencionaron los siguientes especificadores de tipo: ( %spara cadenas, %ppara punteros, %x( %X) para representación hexadecimal, %opara octal.
El tamaño real de los tipos enteros depende de la implementación. La norma solo estipula la relación de tamaño entre tipos y el marco mínimo para cada tipo:
Así long longque no debe ser menos long, que a su vez no debe ser menos int, que a su vez no debe ser menos short. Dado que char es el tipo direccionable más pequeño posible, ningún otro tipo puede ser más pequeño que él.
El tamaño mínimo para char es de 8 bits, para shorty int es de 16 bits, para long es de 32 bits y para long long es de 64 bits.
Es deseable que el tipo intsea un tipo entero con el que el procesador trabaje de manera más eficiente. Esto permite una gran flexibilidad, por ejemplo, todos los tipos pueden ser de 64 bits. Sin embargo, existen esquemas populares que describen los tamaños de los tipos enteros. [7]
En la práctica, esto significa que charrequiere 8 bits en lugar shortde 16 bits (al igual que sus contrapartes sin firmar). inten la mayoría de las plataformas modernas se necesitan 32 bits en lugar long longde 64 bits. La longitud longvaría: para Windows es de 32 bits, para sistemas tipo UNIX es de 64 bits.
El estándar C99 incluye nuevos tipos reales: float_ty double_t, definidos en <math.h>. También incluye tipos complejosfloat _Complex : , double _Complex, long double _Complex.
El tipo booleano se agregó en C99_Bool . Además, un archivo de encabezado adicional <stdbool.h>define un alias para él bool, así como macros true(verdadero) y false(falso). _Boolse comporta como un tipo incorporado normal, con una excepción: cualquier asignación no nula (no falsa) _Boolse almacena como uno. Este comportamiento protege contra el desbordamiento. Por ejemplo:
carácter sin signo b = 256 ; si ( b ) { /* hacer algo */ }bse considera falso si unsigned charocupa 8 bits. Sin embargo, cambiar el tipo hace que la variable sea verdadera:
_Bool b = 256 ; si ( b ) { /* hacer algo */ }La especificación del lenguaje C incluye designaciones de tipo (typedef) size_ty ptrdiff_t. Su tamaño se determina en relación con las capacidades aritméticas del procesador. Ambos tipos se definen en <stddef.h>( cstddefpara C++).
size_t es un tipo entero sin signo diseñado para representar el tamaño de cualquier objeto en la memoria (incluidas las matrices) en una implementación particular. El operador sizeofdevuelve un valor de tipo size_t. El tamaño máximo size_tse escribe en una macro constante SIZE_MAXdefinida en <stdint.h>( cstdintpara C++). size_tdebe tener al menos 16 bits. Además, POSIX incluye ssize_t, que es un tipo de tamaño firmado incorporado size_t.
ptrdiff_t es un tipo con signo integrado que define la diferencia entre los punteros. Se garantiza que actúa sobre punteros del mismo tipo. La aritmética entre punteros de diferentes tipos depende de la implementación.
La información sobre las propiedades reales, como el tamaño, de los tipos integrados básicos se proporciona mediante constantes macro en dos encabezados: un encabezado <limits.h>( climitsen C++) define macros para tipos enteros, un encabezado <float.h>( cfloaten C++) define macros para tipos reales. Los valores específicos dependen de la implementación.
Propiedades de los tipos enterosEl estándar C99 incluye definiciones para varios tipos de enteros nuevos para mejorar la portabilidad del programa. [2] Los tipos base de enteros ya disponibles se consideraron insatisfactorios porque su tamaño dependía de la implementación. Los nuevos tipos son ampliamente utilizados en sistemas integrados. Todos los tipos nuevos se definen en un archivo de encabezado <inttypes.h>( cinttypesen C++) y también están disponibles en <stdint.h>( cstdinten C++). Los tipos se pueden dividir en las siguientes categorías:
La siguiente tabla muestra estos tipos ( N representa el número de bits):
Categoría de tipo | Tipos firmados | tipos sin firmar | ||||
---|---|---|---|---|---|---|
Tipo de | Valor mínimo | Valor máximo | Tipo de | Valor mínimo | Valor máximo | |
Tamaño exacto | intN_t | INTN_MIN | INTN_MAX | uintN_t | 0 | UINTN_MAX |
Talla minima | int_leastN_t | INT_LEASTN_MIN | INT_LEASTN_MAX | uint_leastN_t | 0 | UINT_LEASTN_MAX |
lo más rápido | int_fastN_t | INT_FASTN_MIN | INT_FASTN_MAX | uint_fastN_t | 0 | UINT_FASTN_MAX |
Puntero | intptr_t | INTPTR_MIN | INTPTR_MAX | uintptr_t | 0 | UINTPTR_MAX |
Talla máxima | intmax_t | INTMAX_MIN | INTMAX_MAX | uintmax_t | 0 | UINTMAX_MAX |
El archivo de encabezado <inttypes.h>( cinttypesen C++) amplía las capacidades de los tipos definidos en <stdint.h>. Incluyen macros que definen especificadores de tipo para la cadena de formato printf y scanf, y varias funciones que operan en los tipos intmax_ty uintmax_t. Este archivo de encabezado se agregó en C99 .
cadena de formato printfLas macros se definen en el formato . Aquí {fmt} significa el formato de salida y pertenece a (decimal), (hexadecimal), (octal), (sin signo) o (entero). {tipo} especifica el tipo del argumento y pertenece a , , o , donde es el número de bits. PRI{fmt}{type}dxouiNFASTNLEASTNPTRMAXN
cadena de formato scanfLas macros se definen en el formato . Aquí {fmt} significa el formato de salida y pertenece a (decimal), (hexadecimal), (octal), (sin signo) o (entero). {tipo} especifica el tipo del argumento y pertenece a , , o , donde es el número de bits. SCN{fmt}{type}dxouiNFASTNLEASTNPTRMAXN
FuncionesLas estructuras en C le permiten almacenar múltiples campos en una sola variable. Pueden llamarse registros o tuplas en otros idiomas. Por ejemplo, esta estructura almacena el nombre y la fecha de nacimiento de una persona:
estructura de cumpleaños { nombre del personaje [ 20 ]; día internacional ; mes int ; año int ; };Las declaraciones de estructura en el cuerpo de un programa siempre deben comenzar con la estructura clave (opcional en C++). Se accede a los miembros de la estructura mediante el operador . o -> , si estamos trabajando con un puntero a una estructura. Las estructuras pueden contener punteros a sí mismas, lo que hace posible implementar muchas estructuras de datos basadas en listas enlazadas. Esta posibilidad puede parecer contradictoria, pero todos los punteros ocupan el mismo número de bytes, por lo que el tamaño de este campo no cambiará con el número de campos de estructura.
Las estructuras no siempre ocupan el número de bytes igual a la suma de los bytes de sus elementos. El compilador normalmente alinea los elementos en bloques de 4 bytes. También es posible limitar la cantidad de bits asignados a un campo en particular, para esto debe especificar el tamaño del campo en bits después del nombre del campo, separados por dos puntos. Esta función le permite crear campos de bits .
Algunas características de las estructuras:
Para cada tipo T , a excepción de los tipos de función y vacío, hay un tipo de "matriz de N elementos de tipo T ". Una matriz es una colección de valores del mismo tipo almacenados secuencialmente en la memoria. Una matriz de tamaño N está indexada por un número entero de 0 a N-1 . Las matrices también son posibles, con un tamaño desconocido para el compilador. El tamaño de una matriz debe ser una constante. Ejemplos
gato int [ 10 ] = { 5 , 7 , 2 }; // matriz de 10 elementos, cada uno de tipo int int bob []; // matriz con un número desconocido de elementos de tipo 'int'.Los arreglos se pueden inicializar con una lista de inicialización, pero no se pueden asignar entre sí. Los arreglos se pasan a las funciones usando un puntero al primer elemento (el nombre del arreglo es la dirección del primer elemento). Los arreglos multidimensionales son arreglos de arreglos. Ejemplos:
int a [ 10 ][ 8 ]; // arreglo de 10 elementos, cada uno de tipo 'arreglo de 8 elementos int' float f [][ 32 ] = {{ 0 },{ 4 , 5 , 6 }};Para cualquier tipo T existe un tipo "puntero a T ".
Las variables se pueden declarar como punteros a valores de varios tipos utilizando la extensión *. Para definir el tipo de una variable como un puntero, debe preceder su nombre con un asterisco.
char letra C = 'C' ; char * letra = & letra C ; //tomando la dirección de la letraC y asignándola a la letra printf ( "Este código está escrito en %c." , * letra ); //"Este código está escrito en C."Además de los tipos estándar, puede declarar punteros a estructuras y uniones:
Punto de estructura { int x , y ; } Un ; A._ _ x = 12 ; A._ _ y = 34 _ punto de estructura * p = & A ; printf ( "X: %d, Y: %d" , ( * p ). x , ( * p ). y ); //"X: 12, Y: 34"Para acceder a los campos de una estructura por puntero, existe un operador de flecha ->, sinónimo de la entrada anterior: (*p).x - lo mismo que p->x.
Dado que un puntero también es un tipo de variable, la regla "para cualquier tipo T " también es válida para ellos: puede declarar punteros a punteros. Por ejemplo, puedes usar int***:
intw = 100 ; _ int * x = & w ; int ** y = & x ; int *** z = & y ; printf ( "w contiene %d." , *** z ); //"w contiene 100".También hay punteros a arreglos ya funciones. Los punteros de matriz tienen la siguiente sintaxis:
char * pc [ 10 ]; // array de 10 punteros a char char ( * pa )[ 10 ]; // puntero a una matriz de 10 variables charpc - una matriz de punteros que ocupa un 10 * sizeof(char*)byte (en plataformas comunes, generalmente 40 u 80 bytes), y pa - este es un puntero; suele ocupar 4 u 8 bytes, sin embargo, te permite acceder a un array que ocupa 10 bytes: sizeof(pa) == sizeof(int*)pero sizeof(*pa) == 10 * sizeof(char). Los punteros a matrices difieren de los punteros al primer elemento en aritmética. Por ejemplo, si los punteros paapuntan a la dirección 2000, entonces el puntero pa+1apuntará a la dirección 2010.
char ( * pa )[ 10 ]; matriz de caracteres [ 10 ] = "Wikipedia" ; pa = & matriz ; printf ( "Un ejemplo para %s. \n " , * pa ); //"Un ejemplo para Wikipedia". printf ( "%c %c %c" , ( * pa )[ 1 ], ( * pa )[ 3 ], ( * pa )[ 7 ]); //"ii yo"Las uniones son estructuras especiales que permiten que diferentes campos compartan una memoria común. Por lo tanto, solo uno de los campos se puede almacenar en la unión. El tamaño de la unión es igual al tamaño del campo más grande. Ejemplo:
unión { ent i ; flotante f ; estructura { sin firmar en tu ; doble d ; } s ; } tu ;En el ejemplo anterior u, el tamaño es u.s(cuyo tamaño es la suma de u.s.uy u.s.d), ya que s es mayor que iy f. La lectura de una unión no implica conversiones de tipos.
Las enumeraciones le permiten definir tipos personalizados en su código. Ejemplo:
enumeración { rojo , verde = 3 _ azul } color ;Las enumeraciones mejoran la legibilidad del código, pero no son seguras (por ejemplo, para el sistema 3 y verde son lo mismo. En C ++, las clases de enumeración se introdujeron para corregir esta deficiencia), ya que son números enteros. En este ejemplo, el valor del rojo es cero y el valor del azul es cuatro.
Los punteros de función le permiten pasar una función a otra e implementar un mecanismo de devolución de llamada . Los punteros de función le permiten hacer referencia a funciones con una firma específica. Un ejemplo de creación de un puntero a una función absque toma un int y devuelve un int llamado my_int_f:
int ( * my_int_f )( int ) = & abs ; // el operador & es opcional, pero lo aclara mostrando explícitamente que estamos pasando una direcciónLos punteros de función se llaman por su nombre como llamadas de función normales. Los punteros de función están separados de los punteros regulares y de los punteros a void.
Ejemplo más complejo:
char ret_a ( int x ) { devuelve 'a' + x ; } typedef char ( * fptr )( int ); fptr otra_func ( flotador a ) { volver & ret_a ; }Aquí, por comodidad, hemos creado un alias llamado fptr para un puntero a una función que devuelve un carácter y toma un int. Sin el typedef, la sintaxis sería más difícil de leer:
char ret_a ( int x ) { devuelve 'a' + x ; } char ( * func ( float a , int b ))( int ) { char ( * fp )( int ) = & ret_a ; volver fp ; } char ( * ( * superfunción ( doble a ))( float , int ))( int ) { char ( * ( * fpp )( float , int ))( int ) =& func ; devolver fpp ; }La función func no devuelve un char, como podría parecer, sino un puntero a una función que devuelve un char y acepta un int. Y acepta float e int.
Los tipos anteriores pueden tener diferentes calificadores de tipo. Según el estándar C11 , existen cuatro calificadores de tipo:
Además, desde el estándar 99 , se ha agregado un calificador de función inline, que es una sugerencia para que el compilador incluya código del cuerpo de la función, en lugar de llamar a la función en sí.
Una variable puede tener múltiples calificadores. Ejemplo:
const volátil int a = 5 ; constante int volátil * b = &a ; //puntero a const volatile int int * const c = NULL ; // const puntero a intTambién hay cuatro clases de almacenamiento en C:
lenguaje de programación c | |
---|---|
compiladores |
|
bibliotecas | |
Peculiaridades | |
algunos descendientes | |
C y otros lenguajes |
|
Categoría:Lenguaje de programación C |