C++
std::array: tamaño fijo con seguridad de la STL
El contenedor de tamaño fijo que conserva la eficiencia del array de C y suma los métodos de la Standard Library.
De dónde venimos
Hasta aquí tienes los dos extremos:
- El array de estilo C: rápido y directo, pero de tamaño fijo, sin métodos propios, y que pierde su tamaño al pasarlo a una función.
std::vector: crece en ejecución, lleva su tamaño consigo y gestiona su memoria solo, a cambio de administrar memoria dinámica por dentro.
Falta la opción del medio, y std::array es exactamente eso: el tamaño fijo del array de C, con los métodos y la seguridad de la Standard Library. Cuando sabes de antemano cuántos elementos vas a tener y esa cantidad no va a cambiar nunca, es la elección más precisa de las tres.
Qué es y cómo se declara
Un std::array es una secuencia de tamaño fijo, decidido al compilar, que envuelve al array de estilo C y le agrega los mismos métodos que ya usaste en el vector (.size(), .at(), .empty()). Vive en el header <array>:
#include <array>La declaración lleva dos datos entre ángulos, no uno:
std::array<int, 3> numeros = {1, 2, 3};
// ^^^ ^
// tipo tamaño#include <iostream>
#include <array>
int main() {
std::array<int, 3> numeros = {1, 2, 3}; // 3 enteros, ni uno más
std::cout << "Cantidad de elementos: " << numeros.size() << "\n";
std::cout << "El del medio: " << numeros[1] << "\n";
numeros[1] = 20; // se modifica como un array normal
std::cout << "El del medio ahora: " << numeros[1] << "\n";
return 0;
}g++ -std=c++17 -Wall -o arreglo arreglo.cpp
./arregloCantidad de elementos: 3
El del medio: 2
El del medio ahora: 20Por qué el tamaño va dentro del tipo
Esta es la diferencia conceptual con std::vector, y explica todo lo demás. En un vector, el tamaño es un dato que el objeto guarda y que cambia mientras el programa corre. En un std::array, el tamaño es parte del tipo: std::array<int, 3> y std::array<int, 5> son tipos distintos, tan distintos entre sí como int y double.
De ahí salen tres consecuencias prácticas:
- El tamaño debe conocerse al compilar. Puede ser un número literal o una constante, pero nunca un valor leído por consola.
std::array<int, n> v;connpedido al usuario no compila — para ese caso estástd::vector. - No tiene
.push_back(). No existe “agregar un elemento” a algo cuyo tamaño forma parte de su tipo. Los elementos están todos ahí desde el momento en que se declara. - No usa memoria dinámica. Como su tamaño se conoce al compilar, sus elementos viven donde se declara la variable —normalmente en la pila, igual que un array de estilo C— y no hay ninguna reserva de memoria en el heap de por medio. Eso lo hace tan barato como un array crudo.
Esa sintaxis de <tipo, tamaño> es una plantilla (template) con dos parámetros: uno es un tipo y el otro un número. Es el mismo mecanismo que ya viste en std::vector<int>, solo que aquí se le pasa un dato más. Si la mezcla de <>, {} y [] en una sola línea todavía te marea, hay un repaso dedicado a cada símbolo en Repaso de sintaxis.
Conoce su propio tamaño
Aquí está la ganancia concreta frente al array de estilo C. Recuerda la sorpresa de Arrays y funciones: al pasar int datos[] a una función, el array se degrada a un puntero y la función queda ciega respecto al tamaño, así que hay que mandarlo aparte en un segundo parámetro.
Un std::array no se degrada: es un objeto que se pasa entero, con su tamaño incluido en el tipo.
#include <iostream>
#include <array>
// No hace falta un segundo parámetro con la cantidad: viaja dentro del tipo
void mostrar(const std::array<int, 5>& datos) {
std::cout << "Recibí " << datos.size() << " elementos: ";
for (int valor : datos) {
std::cout << valor << " ";
}
std::cout << "\n";
}
int main() {
std::array<int, 5> numeros = {2, 4, 6, 8, 10};
mostrar(numeros);
return 0;
}Recibí 5 elementos: 2 4 6 8 10 Dentro de mostrar(), datos.size() vale 5 y el for basado en rango funciona sin más: dos cosas imposibles con un int datos[].
El precio de esa garantía es que la función solo acepta arrays de ese tamaño exacto: mostrar() no compilará si le pasas un std::array<int, 3>. No es un descuido, es el mismo compromiso de siempre — más rigidez a cambio de que el compilador detecte los errores en vez de descubrirlos en ejecución. Si necesitas una función que acepte cualquier cantidad de elementos, usa std::vector.
El parámetro va, otra vez, como referencia constante (const std::array<int, 5>&). Sin el &, el std::array se copiaría entero al llamar a la función, a diferencia del array de estilo C, que nunca se copia porque se degrada a puntero. Es un cambio de comportamiento fácil de pasar por alto al migrar código viejo.
Acceso seguro e inseguro a los elementos
Idéntico a lo que viste en vector, y esa es justamente la gracia: los métodos son los mismos en toda la Standard Library.
#include <iostream>
#include <array>
int main() {
std::array<int, 3> numeros = {10, 20, 30};
std::cout << "Con []: " << numeros[2] << "\n"; // sin comprobación
std::cout << "Con .at(): " << numeros.at(2) << "\n"; // con comprobación
return 0;
}Con []: 30
Con .at(): 30numeros[5] en un std::array<int, 3> es comportamiento indefinido, exactamente igual que en un array crudo o en un vector: compila sin quejarse y hace lo que sea. .at(5) detiene el programa con un mensaje que dice qué índice pediste y cuántos elementos había. Ninguno de los tres contenedores de este grupo comprueba los límites con [] — la seguridad hay que pedirla escribiendo .at().
Ejemplo completo: los días de la semana
El caso típico de std::array: una colección cuyo tamaño es un hecho del mundo, no una decisión de programa. Los días de la semana son siete y no van a ser ocho mañana.
#include <iostream>
#include <array>
#include <string>
int main() {
const std::array<std::string, 7> dias = {
"lunes", "martes", "miércoles", "jueves",
"viernes", "sábado", "domingo"
};
std::cout << "La semana tiene " << dias.size() << " días:\n";
for (const std::string& dia : dias) {
std::cout << "- " << dia << "\n";
}
std::cout << "El tercer día es " << dias.at(2) << "\n";
return 0;
}g++ -std=c++17 -Wall -o semana semana.cpp
./semanaLa semana tiene 7 días:
- lunes
- martes
- miércoles
- jueves
- viernes
- sábado
- domingo
El tercer día es miércolesDos detalles del ejemplo:
- El
constde la primera línea dice que la colección entera no se va a modificar. Con unstd::arrayde contenido fijo es lo habitual, y hace que el compilador rechace cualquier intento accidental de escribir en él. dias.at(2)devuelve"miércoles", el tercer elemento: los índices siguen empezando en 0, igual que en todo lo demás.
Tabla resumen: array de C, std::array y std::vector
Array de C (int v[5]) | std::array<int, 5> | std::vector<int> | |
|---|---|---|---|
| Tamaño | Fijo, decidido al compilar | Fijo, decidido al compilar (parte del tipo) | Variable, cambia en ejecución con .push_back() |
| Conoce su tamaño | Solo donde se declara; se pierde al pasarlo a una función | Siempre, con .size() | Siempre, con .size() |
| Dónde vive en memoria | Donde se declara (pila, si es una variable local) | Donde se declara (pila, si es una variable local) | Los elementos en el heap, pedidos y liberados solos |
| Cuándo usarlo | Cuando una API de C o una librería te obliga | Cantidad fija y conocida al compilar: días de la semana, coordenadas de un punto, un tablero de 8×8 | Todo lo demás. Es la opción por defecto cuando no sabes cuántos elementos habrá |
Los tres comparten la misma forma de indexar ([]), el mismo for basado en rango y —los dos de la Standard Library— los mismos métodos. Eso es deliberado: cambiar de uno a otro suele ser cuestión de tocar la línea de la declaración, no de reescribir la lógica que los usa.
Hasta aquí los contenedores básicos
Con std::vector y std::array ya tienes cubierto el caso general de “guardar muchos valores del mismo tipo”, que es la necesidad más frecuente del día a día. Los otros contenedores que aparecieron en el mapa de la Standard Library —std::map para asociar claves con valores, std::set para valores únicos— resuelven necesidades más específicas y se apoyan en las mismas ideas que acabas de ver: un objeto que guarda otros objetos, conoce su tamaño y administra su propia memoria.
La documentación continúa con Dear ImGui, una librería externa para construir interfaces gráficas. Ahí volverás a encontrarte con casi todo lo de estas páginas: std::vector para las listas de elementos que se dibujan en pantalla, arrays de estilo C en las funciones que vienen de su API en C, y std::string con .c_str() para pasarle textos.