sizeof es un operador, no una función. Esto es fundamental: en la mayoría de los casos, el compilador calcula su valor durante la fase de compilación e inserta el número directamente en el código máquina. Su propósito es devolver el tamaño en bytes que ocupa un tipo de dato o una expresión en la memoria. Para garantizar que el valor sea capaz de representar el tamaño de cualquier objeto posible, el operador devuelve siempre un tipo size_t [stddef.h], un entero sin signo diseñado específicamente para este fin. Al imprimir este valor con printf, la forma correcta y portable es usar el especificador de formato %zu.
Cuando aplicas sizeof a un array, obtienes el tamaño total en bytes de todo el bloque de memoria que ocupa. Para calcular cuántos elementos contiene realmente ese array, la técnica estándar es dividir el tamaño total entre el tamaño de su primer elemento: sizeof(nombre_array) / sizeof(nombre_array[0]). No obstante, debes tener cuidado con la “degradación a puntero”: si pasas un array a una función, este se convierte en un puntero. Dentro de esa función, sizeof te devolverá el tamaño del puntero (normalmente 8 bytes en sistemas de 64 bits) y no el tamaño del array original. Finalmente, ten presente que el tamaño de una struct puede ser mayor que la suma de sus componentes debido al padding (relleno) que el compilador inserta para alinear los datos en memoria, un comportamiento ligado a la alineación de memoria que puedes consultar con _Alignof [C11].
#include <stdio.h>
#include <stddef.h>
// La estructura tendrá padding para alinear el 'int'
struct Datos {
char a; // 1 byte
// El compilador inserta 3 bytes de padding aquí
int b; // 4 bytes
char c; // 1 byte
// El compilador inserta 3 bytes de padding aquí para completar el alineamiento
};
// Esta función recibe un puntero, no el array completo
void analizar_puntero(int *ptr) {
// Error común: sizeof(ptr) devuelve el tamaño del puntero, no del array
printf(" [Dentro de la función] sizeof(ptr) = %zu bytes\n", sizeof(ptr));
}
int main() {
// 1. Tipos básicos
printf("--- Tipos básicos ---\n");
printf("Tamaño de char: %zu byte\n", sizeof(char));
printf("Tamaño de int: %zu bytes\n", sizeof(int));
printf("Tamaño de double: %zu bytes\n\n", sizeof(double));
// 2. Estructuras y el concepto de padding
printf("--- Estructuras y alineación ---\n");
struct Datos d;
printf("Tamaño de la struct Datos: %zu bytes (con padding)\n\n", sizeof(d));
// 3. Arrays y cálculo de elementos
printf("--- Arrays ---\n");
int numeros[] = {10, 20, 30, 40, 50};
size_t total_bytes = sizeof(numeros);
size_t num_elementos = total_bytes / sizeof(numeros[0]);
printf("Array 'numeros': %zu bytes en total\n", total_bytes);
printf("Número de elementos: %zu\n\n", num_elementos);
// 4. El problema de la degradación a puntero
printf("--- Degradación a puntero ---\n");
analizar_puntero(numeros);
printf("\n");
// 5. Variable Length Arrays (VLA) [C99]
// El tamaño de un VLA se calcula en tiempo de ejecución
printf("--- VLA (Variable Length Array) ---\n");
int n = 5;
int vla_array[n];
printf("Tamaño de vla_array con n=%d: %zu bytes\n", n, sizeof(vla_array));
return 0;
}
Desglose del código
En el ejemplo, observa cómo sizeof(int) devuelve 4 bytes, pero al analizar la struct Datos, el resultado de sizeof(d) es 12 bytes en lugar de los 6 bytes que sumarían sus miembros (char + int + char). Esto ocurre porque el compilador inserta padding para que el miembro int b esté alineado en una dirección de memoria múltiplo de 4, y para que el tamaño total de la estructura sea un múltiplo que facilite el acceso en arrays de estructuras.
Cuando trabajamos con el array numeros, sizeof(numeros) devuelve el tamaño total en bytes (20 bytes, asumiendo que int mide 4). Al dividirlo por sizeof(numeros[0]), obtenemos correctamente 5. Sin embargo, fíjate en la función analizar_puntero. Aunque le pasamos el array numeros, la variable ptr es solo un puntero a su primer elemento. Por tanto, sizeof(ptr) devuelve el tamaño del puntero en la arquitectura actual (8 bytes en sistemas de 64 bits), perdiendo la información de cuántos elementos tiene el array original.
Por último, el vla_array es un Variable Length Array [C99]. A diferencia de los arrays estáticos, su tamaño depende de una variable n definida en tiempo de ejecución. Por esta razón, sizeof(vla_array) no puede ser determinado por el compilador durante la fase de compilación y se evalúa en el momento exacto en que se ejecuta esa línea de código.
El error frecuente
Un error clásico es intentar calcular el tamaño de un array dentro de una función que lo recibe como parámetro:
void error_logica(int arr[]) {
// error_logico: arr es un puntero, sizeof(arr) NO es el tamaño del array
size_t tamaño = sizeof(arr);
printf("El array tiene %zu elementos\n", tamaño / sizeof(arr[0]));
}
Este código no producirá un error de compilación, pero el resultado será erróneo (usualmente 2 o 4 elementos en sistemas de 64/32 bits respectivamente). Este error es una fuente común de desbordamientos de memoria (buffer overflows) cuando se utiliza ese tamaño incorrecto para recorrer el array. Herramientas como AddressSanitizer detectarán el desbordamiento cuando intentes acceder a un índice más allá del tamaño real, pero no te dirán que el error fue un mal uso de sizeof.
N° 15