6 – Matrices

En C, las matrices o arrays bidimensionales son estructuras de datos que nos permiten almacenar datos en una forma tabular, con filas y columnas. Se definen como una colección de elementos dispuestos en filas y columnas, y cada elemento se puede acceder mediante un par de índices que indican su posición en la matriz.

Para declarar e inicializar una matriz bidimensional en C, se utiliza la siguiente sintaxis:

tipo_de_dato nombre_de_la_matriz[num_filas][num_columnas] = {
    {fila0_col0, fila0_col1, ..., fila0_colN},
    {fila1_col0, fila1_col1, ..., fila1_colN},
    ...
    {filaM_col0, filaM_col1, ..., filaM_colN}
};



Por ejemplo:
 int num_filas = 3,num_columnas = 3;
 int m[num_filas][num_columnas];

Formas de Inicializar una matriz

int m1[3][3] = {{1,3},{5,6,7},{9,6,3}}; // En este caso si faltan elementos se completan con ceros.

int m2[3][3] = {{1,3},{5,6,7},{9,6,3,5}}; // Si sobran elementos los ignora.

int m3[3][3] = {{0}}; // Toda Con ceros

int m4[3][3] ; // Inicializa con basura

int m5[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};

int m6[][3] = {1,2,3,4,5,6,7,8,9};

int m7[][3] = {{1,2,3},{4,5,6},{7,8,9}};

int m8[][3] = {{1},{2,3},{4,5,6},{7,8},{9}};

imprimir(m2);

En estos últimos ejemplos, m6 y m7 son idénticos a m5, pero m8 resulta:

Lo que sucede con m8 es que al no haber indicado explícitamente los valores de toda una fila, éstos se llenaron con ceros (depende del compilador).

Con mat6, al no haber indicado cuántas filas se iban a usar, el compilador las crea automáticamente según se necesite, como se ingresaron 9 valores y habían 3 columnas, se crearon 3 filas, pero si:

int mat5[][3] = {1,2,3,4,5};

Esta línea daría como resultado una matriz de 2×3.

void imprimir(int mat[3][3])
{
    int f,c;
    for(f=0;f<3;f++)
    {
        for(c=0;c<3;c++)
        {
            printf("%i ",mat[f][c]);
        }
        printf("\n");
    }
}

Para ver un ejemplo completo:

int main()
{
 int num_filas = 3,num_columnas = 3;
 int m[num_filas][num_columnas];
 imprimir(m);
 cargar(m);
 imprimir(m);
 return 0;
}


 void cargar(int mat[3][3])
{
    int f,c;
    for(f=0;f<3;f++)
    {
        for(c=0;c<3;c++)
        {
            printf("Ingrese componente:");
            scanf("%i",&mat[f][c]);
        }
    }
}

void imprimir(int mat[3][3])
{
    int f,c;
    for(f=0;f<3;f++)
    {
        for(c=0;c<3;c++)
        {
            printf("%i ",mat[f][c]);
        }
        printf("\n");
    }
}

TENER EN CUENTA QUE SIEMPRE ES NECESARIO ESPECIFICAR LAS COLUMNAS, no es necesario especificar las filas. Esto es porque en realidad C, guarda todos los elementos como si fueran un array, en posiciones contiguas. Esto se puede corroborar haciendo lo siguiente:

int m2[][3] = {{1,3},{5,6,7},{9,6,3,5}}; // Borro filas, el codigo sigue funcionando correctamente.

int m2[3][] = {{1,3},{5,6,7},{9,6,3,5}}; // Borro columnas, el compilador arroja error.

int m2[][]; // Borro columnas y filas, el compilador arroja error.


EJERCICIOS CON MATRICES

Para hacer estos ejercicios, lo ideal es que se haga un menu con un do while y switch para cada una de las opciones.

Además debemos enviar la matriz con el total de las columnas COL, y filas y columnas. COL es el total de la matriz, lo ideal siempre es hacer una matriz muy muy grande y las filas y columnas que se pasan por parámetro son las filas y columnas que se van a utilizar.

Esto es para que el día de mañana si yo quiero usar estas funciones con otras matrices no necesito reescribir todo el código sino que solo le envío filas y columnas como parámetro y listo.

Ejemplo:

void mostrar_matriz(int m[][COL], int f, int c)
{
    int i, k;
    for (i=0; i<f; i++)
    {
        for (k=0; k<c; k++)
        {
            printf ("[%d]",m[i][k]);
        }
        printf("\n");
    }
}

Direccionamiento

Al igual que con vectores, se pueden usar punteros o índices, aunque en los ejercicios de la cátedra se usarán índices. 

Es importante entender cómo se guarda la matriz en memoria, siguiendo con el ejemplo anterior, la matriz de 3×3 mat se guarda de la siguiente manera:

Pareciera guardarse como un vector, entonces ¿dónde están las columnas?, básicamente esa es la razón por la cual es necesario especificarlas en su declaración. La memoria del sistema no se divide en columnas, por lo que al declarar una matriz, ésta se guarda en memoria contigua.

Para acceder por ejemplo al dato entero 4 de la matriz, uno podría usar índices de la siguiente manera:

int cuatro = mat[1][0];

Donde índice 1 es la fila, que al igual que los vectores inicia en cero, y el índice 0 es la columna. Ésta expresión es equivalente en puntero:

int* ptr = mat;

int cuatro = *(ptr + 1*3 + 0);

Vamos por partes: mat es un puntero constante a la primer posición de la matriz mat, por lo que el puntero ptr pasa a apuntar a la primer posición de la matriz (en éste caso B000), luego se le suma 1*3 (en éste caso, pasa a apuntar a B00C), donde 1 es la cantidad de filas a desplazar, y 3 es la cantidad de columnas, nuevamente queda en evidencia la necesidad de especificar a el compilador la cantidad de columnas antes de compilar, y por último se suman la cantidad de desplazamientos de columna que en este caso son cero.

Es importante notar que mat es un puntero a puntero, y por ello en el ejemplo anterior fue necesario usar un puntero “ptr” y no se puede simplemente sumarle a los desplazamientos a mat.

En el ejemplo en el cual venimos trabajando, si uno llamase al contenido de mat:

printf(“%p”,*mat); //B000

La salida sería B000, pues mat es un puntero que apunta al primer elemento de la matriz. Si uno quisiera acceder al primer elemento de la matriz desde mat, debería

printf(“%p”, **mat); //1

La salida de ésto daría por resultado el contenido dentro de la posición B000. Notar que nada tiene que ver la dirección de mat en sí misma, que es la dirección del primer elemento de la matriz.

printf(“%x”, &mat); //B000

Por último, notar que 

printf(“%p”, &mat); //B000

printf(“%p”, mat[0]); //B000

printf(“%p”, &mat[0][0]); //B000

printf(“%p”, &mat[0]); //B000

printf(“%p”, mat); //B000

printf(“%d”, **mat); //1

printf(“%d”, *mat[0]); //1

printf(“%d”, mat[0][0]); //1

Funciones

Al declarar una función:

void f(int mat[][columnas]);

Al llamar a la función:

f(mat);

El compilador controla que la dimensión de la matriz mat sea menor o igual con la esperada en el prototipo de la función. Para evitar tener que programar una misma función para cada dimensión de matriz, se puede declarar una función que reciba matrices de 100 filas y 100 columnas, y pasarle como argumento una matriz de 100 columnas y sólo usar un número determinado de columnas, el cual también debería pasarse como argumento.