Cada variable en C tiene un tipo, elegido cuando la declaras y fijo durante toda su vida. El tipo decide tres cosas: cuántos bytes ocupa la variable, cómo se interpretan esos bytes y qué operaciones tienen sentido sobre ella.
La lista de tipos de C es corta. La complejidad está en los modificadores.
Los cuatro tipos base
charguarda un byte. Se usa para caracteres individuales ('A'), para los bytes de una cadena y a veces como un entero diminuto.intes el tipo de número entero de uso general. Contadores de bucles, tamaños, identificadores.floatydoubleguardan números reales.doubletiene aproximadamente el doble de precisión.voides el cuarto tipo en cierto sentido, pero significa "sin valor": no puedes declarar una variablevoid, solo usarlo como tipo de retorno de una función, como lista de parámetros vacía o como puntero sin tipo.
Fíjate en el sufijo f de 3.14f. Sin él el literal es un double, y asignarlo a un float pierde precisión en silencio. Algunos compiladores lo advierten.
Modificadores: short, long, unsigned
Los tipos enteros base vienen con modificadores que cambian el tamaño o el signo.
Cada tipo entero necesita su propio especificador de formato: %hd para short, %d para int, %ld para long, %lld para long long, %u para unsigned. Usar el equivocado es comportamiento indefinido, no un error de redondeo.
unsigned elimina el bit de signo y duplica el rango positivo. Un unsigned int guarda de 0 a unos 4.300 millones en lugar de -2.100 a +2.100 millones. Es la elección correcta para cosas que genuinamente no pueden ser negativas — tamaños, conteos de bytes, patrones de bits — y una trampa para cualquier cosa a la que se le pueda restar por debajo de cero.
signed es el valor por defecto para int, short, long y long long, así que casi nunca lo escribes. El único lugar donde importa es char: si un char simple es con signo o sin signo queda definido por la implementación, así que escribe signed char o unsigned char cuando el signo importe.
Tamaños: lo que realmente obtienes
El estándar de C especifica mínimos, no tamaños exactos. En cualquier máquina moderna de 64 bits con Linux, macOS o Windows verás esto:
| Tipo | Tamaño típico | Rango típico |
|---|---|---|
char | 1 byte | -128 a 127 (o 0 a 255) |
short | 2 bytes | -32.768 a 32.767 |
int | 4 bytes | -2.147.483.648 a 2.147.483.647 |
long | 8 bytes (4 en Windows) | aproximadamente ±9,2 trillones |
long long | 8 bytes | aproximadamente ±9,2 trillones |
float | 4 bytes | ~7 dígitos significativos |
double | 8 bytes | ~15 dígitos significativos |
long double | 16 bytes (varía) | más que double |
La fila de long es la que agarra desprevenida a la gente: son 8 bytes en Linux y macOS, y 4 bytes en Windows de 64 bits. El código que asume que long puede guardar un valor de 64 bits no es portable. Usa long long, o los tipos de ancho exacto de stdint.h (int32_t, uint64_t) cuando el tamaño sea parte del requisito.
sizeof: pregúntale al compilador
Nunca adivines un tamaño: mídelo.
sizeof es un operador, no una función, y se evalúa en tiempo de compilación. Produce un valor de tipo size_t, que se imprime con %zu.
sizeof(char) está garantizado que es exactamente 1: esa es la definición de byte en C. Todo lo demás se mide en relación con eso.
limits.h y float.h
Los rangos exactos de tu compilador están disponibles como constantes con nombre:
Estos son los valores con los que comparar cuando necesitas saber si una operación se va a desbordar. Comprobar if (a > INT_MAX - b) antes de calcular a + b es la forma de detectar el desbordamiento antes de que ocurra, lo cual importa, porque no puedes detectar el desbordamiento con signo después.
Desbordamiento de enteros
Qué pasa cuando un valor excede el rango de su tipo depende por completo del signo.
El desbordamiento sin signo está definido: el valor da la vuelta módulo 2^N.
Ese segundo caso es una fuente real de errores. Un bucle como for (unsigned i = n - 1; i >= 0; i--) nunca termina, porque un valor sin signo siempre es >= 0.
El desbordamiento con signo es comportamiento indefinido. No "da la vuelta": indefinido. Al compilador se le permite asumir que nunca ocurre y optimizar sobre esa base, lo que significa que una comprobación de desbordamiento escrita después del hecho puede ser eliminada:
int sum = a + b;
if (sum < a) { /* el compilador puede eliminar esto por completo */ }
Compruébalo antes, usando los límites:
if (b > 0 && a > INT_MAX - b) {
/* a + b se desbordaría: manéjalo */
}
Precisión de punto flotante
float y double guardan los números en binario, y la mayoría de las fracciones decimales no tienen forma binaria exacta, de la misma manera que 1/3 no tiene forma decimal exacta.
La regla que se deduce: nunca compares valores de punto flotante con ==. Compara en cambio la diferencia absoluta contra una tolerancia pequeña.
Y nunca uses punto flotante para dinero. Guarda los centavos como un entero; un long long de centavos es exacto donde un double de pesos no lo es.
Elegir un tipo
Una lista corta de decisiones que cubre la mayor parte del código:
- Números enteros:
inta menos que tengas una razón. Es el tipo que la CPU maneja con más eficiencia y el tipo alrededor del cual están construidas todas las reglas aritméticas. - Cualquier cosa mayor a 2.000 millones:
long long, oint64_tdestdint.h. - Tamaños, longitudes e índices de arrays provenientes de
sizeofostrlen:size_t. Es sin signo y está garantizado que es lo bastante grande para cualquier objeto. - Decimales:
double. Usafloatsolo para reducir a la mitad la memoria en arrays grandes o en hardware embebido sin unidad de doble precisión. - Caracteres individuales y bytes crudos:
charpara texto,unsigned charpara datos binarios. - Verdadero/falso:
booldestdbool.h— mira booleanos en C. - Anchos de bits exactos (formatos de archivo, protocolos de red, registros de hardware):
stdint.h—uint8_t,int16_t,uint32_t, y así sucesivamente.
Mezclar tipos
Cuando combinas dos tipos distintos en una misma expresión, C los convierte a tus espaldas antes de hacer la aritmética. Eso suele ser útil y ocasionalmente desastroso:
La primera es división entera: ambos operandos son int, así que el resultado es un int y la fracción se descarta. La tercera es peor: comparar un valor con signo con uno sin signo convierte el que tiene signo a sin signo, convirtiendo -1 en un número positivo enorme.
Esas reglas de conversión, y cómo tomar el control de ellas con conversiones explícitas, son el tema de conversión de tipos en C.
Preguntas frecuentes
¿Cuáles son los tipos de datos básicos en C?
Cuatro tipos base: char para caracteres individuales y bytes, int para números enteros, y float y double para decimales. Los modificadores cambian su tamaño y su signo — short, long, long long, signed y unsigned — produciendo el conjunto completo.
¿Cuántos bytes ocupa un int en C?
Casi siempre 4 bytes (32 bits) en sistemas modernos de escritorio y servidores, lo que da un rango de aproximadamente -2.100 millones a 2.100 millones. El estándar solo garantiza al menos 2 bytes, y los microcontroladores de 16 bits sí usan 2. Usa sizeof(int) si necesitas saberlo con certeza.
¿Cuál es la diferencia entre float y double en C?
float ocupa 4 bytes con alrededor de 7 dígitos decimales significativos; double ocupa 8 bytes con unos 15. Double es el tipo por defecto de los literales de punto flotante y de las funciones matemáticas, y salvo que estés guardando millones de valores o apuntando a un chip embebido, double es la elección correcta.
¿Qué pasa cuando un int se desborda en C?
Para un int con signo, el desbordamiento es comportamiento indefinido: el compilador puede dar la vuelta, saturar o eliminar por completo la comprobación al optimizar. Para un int sin signo está totalmente definido: el valor da la vuelta módulo 2^N, así que UINT_MAX + 1 es 0. Nunca te apoyes en el desbordamiento con signo.