Cargando...

Arreglos multidimensionales y punteros

Arreglos 2D/3D, disposición row-major y cálculo de índices con punteros en C++.

En C++, los arreglos multidimensionales (por ejemplo, las matrices) se utilizan para organizar datos en forma de tabla o cuadrícula. Los arreglos multidimensionales se almacenan de forma contigua en la memoria y se pueden acceder directamente mediante punteros. En este artículo aprenderemos la relación entre los arreglos 2D y 3D y los punteros, las técnicas de acceso y cómo crear matrices dinámicas.


1. Arreglos bidimensionales (2D)

Los arreglos bidimensionales generalmente se representan como filas y columnas. El siguiente ejemplo define una matriz de 2 filas × 3 columnas:


#include <iostream>
using namespace std;

int main() {
    int matriz[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };

    cout << matriz[0][1] << endl; // 2
    cout << matriz[1][2] << endl; // 6
}

En este ejemplo, matriz[0][1] representa el valor en la primera fila, segunda columna. Dado que los arreglos en C++ comienzan en 0, [1][2] corresponde en realidad a la segunda fila, tercera columna.


2. Disposición en memoria

C++ almacena los arreglos multidimensionales en memoria siguiendo el orden row-major (por filas). Esto significa que cada fila se almacena de manera continua en la memoria:


Disposición en memoria:
matriz[0][0], matriz[0][1], matriz[0][2],
matriz[1][0], matriz[1][1], matriz[1][2]

Gracias a esto, se puede acceder a todos los elementos utilizando aritmética de punteros como si fuera un arreglo unidimensional.


3. Acceso a elementos de un arreglo 2D con punteros

Los arreglos multidimensionales son esencialmente cadenas de punteros. Por ejemplo, en int matriz[2][3], matriz contiene la dirección de un arreglo int[3].


#include <iostream>
using namespace std;

int main() {
    int matriz[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };

    cout << *(*(matriz + 0) + 1) << endl; // 2
    cout << *(*(matriz + 1) + 2) << endl; // 6
}


4. Recorrido con punteros

Es posible recorrer los elementos de una matriz utilizando aritmética de punteros.


int matriz[2][3] = {{10,20,30},{40,50,60}};
int *ptr = &matriz[0][0];

for (int i = 0; i < 6; i++) {
    cout << *(ptr + i) << " ";
}

Salida:


10 20 30 40 50 60

Como se puede ver, una matriz 2D se almacena como un solo bloque continuo en memoria, por lo que es posible acceder a todos los elementos con un solo puntero.


5. Pasar arreglos multidimensionales a funciones

Los arreglos 2D pueden pasarse como parámetros a funciones. Sin embargo, en C++, es obligatorio especificar la segunda dimensión (el número de columnas).


void Imprimir(int arr[][3], int filas) {
    for (int i = 0; i < filas; i++) {
        for (int j = 0; j < 3; j++) {
            cout << arr[i][j] << " ";
        }
        cout << endl;
    }
}

int main() {
    int matriz[2][3] = {{1,2,3},{4,5,6}};
    Imprimir(matriz, 2);
}

Alternativamente, se puede usar la sintaxis de punteros:


void Imprimir(int (*p)[3], int filas) {
    for (int i = 0; i < filas; i++)
        for (int j = 0; j < 3; j++)
            cout << p[i][j] << " ";
}

Aquí, int (*p)[3] significa “puntero a un arreglo de 3 enteros”.


6. Arreglos tridimensionales (3D)

Los arreglos 3D son colecciones de arreglos 2D. Por ejemplo, int espacio[2][3][4] representa 2 matrices de tamaño 3×4.


#include <iostream>
using namespace std;

int main() {
    int espacio[2][3][4] = {
        { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} },
        { {13,14,15,16}, {17,18,19,20}, {21,22,23,24} }
    };

    cout << espacio[1][2][3] << endl; // 24
}

Los arreglos 3D se utilizan comúnmente en procesamiento gráfico, motores de juegos y cálculos científicos.


7. Creación dinámica de una matriz 2D

Para crear matrices cuyo tamaño no se conoce en tiempo de compilación, se puede usar el operador new junto con arreglos de punteros.


#include <iostream>
using namespace std;

int main() {
    int filas = 2, columnas = 3;
    int **matriz = new int*[filas]; // arreglo de punteros de filas

    for (int i = 0; i < filas; i++)
        matriz[i] = new int[columnas]; // crear cada fila

    // asignar valores
    for (int i = 0; i < filas; i++)
        for (int j = 0; j < columnas; j++)
            matriz[i][j] = (i+1)*(j+1);

    // imprimir
    for (int i = 0; i < filas; i++) {
        for (int j = 0; j < columnas; j++)
            cout << matriz[i][j] << " ";
        cout << endl;
    }

    // liberar memoria
    for (int i = 0; i < filas; i++)
        delete[] matriz[i];
    delete[] matriz;
}

Con este método, el tamaño de la matriz puede determinarse dinámicamente (por ejemplo, mediante la entrada del usuario). Pero recuerda: cada new[] debe tener un delete[] correspondiente.


8. Alternativa moderna: std::vector<vector<int>>

En C++, la forma más segura y sencilla de crear una matriz dinámica es usar std::vector.


#include <iostream>
#include <vector>
using namespace std;

int main() {
    int filas = 2, columnas = 3;
    vector<vector<int>> matriz(filas, vector<int>(columnas));

    for (int i = 0; i < filas; i++)
        for (int j = 0; j < columnas; j++)
            matriz[i][j] = (i+1) + (j+1);

    for (auto &fila : matriz) {
        for (int valor : fila)
            cout << valor << " ";
        cout << endl;
    }
}

std::vector gestiona automáticamente la memoria, no requiere delete[] ni new, y su tamaño se puede modificar fácilmente.


9. TL;DR

  • Los arreglos 2D se almacenan en memoria fila por fila (orden row-major).
  • *(matriz + i) + j es equivalente a matriz[i][j].
  • En los parámetros de función se debe especificar la segunda dimensión.
  • Los arreglos 3D son conjuntos de arreglos 2D: int espacio[2][3][4].
  • Las matrices dinámicas pueden crearse con new / delete[].
  • En C++ moderno, se recomienda std::vector<vector<>>.
  • Todos los ejemplos pueden ejecutarse en Visual Studio 2022 o GCC 11+.

Artículos relacionados