- Contenido-

El lenguaje C permite el uso de matrices, es decir, arrays bidimensionales. En general, todo lo que vamos a estudiar en esta sección se puede extrapolar a arrays incluso n-dimensionales (o tensores), aunque en la práctica el uso de arrays con más de 2 dimensiones no es muy común. La declaración de una matriz o array bidimensional es:

tipo variable_matriz[N][M]; 

Donde N y M son el número de filas y de columnas respectivamente (la dimensión de la matriz). Se ha escrito la dimensión con letras mayúsculas, ya que deben ser constantes, y al igual que con vectores se suelen definir con constantes, por ejemplo:

#define N 4 //número de filas de las matrices que voy a declarar
#define M 5 //número de columnas de las matrices que voy a declarar

main()
{
	double matriz1[N][M], matriz2[N][M];
	int matriz_entera[N][M];
	...
}

Al igual que con vectores, las matrices se numeran empezando por el índice 0, con lo cual el elemento superior izquierdo es el [0][0] y el inferior derecho es el [N-1][M-1]. En la siguiente tabla se muestra cual sería la forma y los elementos de una matriz a[4][5], de tamaño 4×5. Véase que el primer elemento del array bidimensional es el a[0][0], el siguiente sería el a[0][1], y así, hasta llegar al elemento a[3][4].

Matriz bidimensional
Elemento en Índice de columna
0 1 2 3 4
Índice de fila 0 a[0][0] a[0][1] a[0][2] a[0][3] a[0][4]
1 a[1][0] a[1][1] a[1][2] a[1][3] a[0][4]
2 a[2][0] a[2][1] a[2][2] a[2][3] a[0][4]
3 a[3][0] a[3][1] a[3][2] a[3][3] a[0][4]

Por otra parte, en lenguaje C las matrices se almacenan en memoria "por filas", es decir, los elementos de la fila primera (de índice 0) van consecutivos y cuando acaba el último de la primera fila empieza a almacenarse el primero de la segunda, y así sucesivamente hasta llegar a la última fila. La Figura 4.4 muestra como estaría almacenada en la memoria la matriz anterior a[4][5]. Hay que aclarar que algunos lenguajes de programación (FORTRAN) utilizan el convenio contrario, es decir, almacenan las matrices por columnas.

Distribución de una matriz bidimensional en memoria
Fila 0 1 2 3
En memoria a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3] a[3][0] a[3][1] a[3][2] a[3][3]

Aunque se puede trabajar con elementos por separado de una matriz, lo habitual es hacer operaciones matriciales con todos los elementos en conjunto. Al igual que no existen operadores vectoriales en lenguaje C, no existen tampoco operadores matriciales, de manera que tales operaciones hay que realizarlas con bucles. Como ya vimos, el anidamiento de bucles permite el recorrido completo de un array bidimensional o matriz, y tal recorrido puede hacerse por filas o por columnas. A continuación se muestra un sencillo algoritmo que nos permite recorrer por filas completamente matrices (ya que el bucle interno actúa sobre los índices de las columnas); en primer lugar se valoran las matrices b y c con ciertos números y seguidamente se realiza la operación matricial suma (en matemáticas se define un operador suma matricial con el mismo símbolo '+', de forma que este algoritmo sería equivalente a la suma de matrices A=B+C).

int a[4][5], b[4][5], c[4][5], i, j;

// se valoran las matrices origen A y B con ciertos números.
for (i = 0 ; i < 4 ; i++)
	for (j = 0 ; j < 5 ; j++)
	{
		b[i][j] = i;
		c[i][j] = (i * j) + j;
	}
// A continuación se realiza la suma matricial 
for (i = 0 ; i < 4 ; i++)
	for (j = 0 ; j < 5 ; j++)
	{
		a[i][j] = b[i][j] + c[i][j] ;
	}

Además del recorrido simple por filas o por columnas, pueden existir otros bucles anidados que mezclen recorridos por filas con recorridos con columnas. Esto ocurre, por ejemplo, en el producto matricial (en matemáticas A=B×C), ya que en tal operación es necesario que una fila de B se multiplique por una columna de C (en los ejercicios de este apartado se estudia esta operación).

Hay que tener en cuenta que, por un lado, la declaración de matrices implica una reserva o consumo de memoria que puede ser importante, y por otro, que el uso posterior de las mismas en bucles anidados puede suponer un tiempo de ejecución considerable. Por ejemplo si es pretende trabajar con una matriz donde se almacena un elemento tipo double por cada píxel de la pantalla de alta resolución, esto supone declarar una matriz de dimensión [1024][768], es decir, un consumo de 8×1024×768=6,291,456 bytes, o sea, más de 6 MB. Posteriormente cada vez que se ejecute un bucle anidado para recorrer tal matriz, ¡se ejecutarán 1024×768=786,432 iteraciones!

Por último y como explicamos antes, se puede extrapolar todo lo anterior a arrays n-dimensionales. Así, el siguiente ejemplo declara un tensor de dimensión 4×5×6 e inicializa todos sus elementos a cero usando tres bucles anidados:

double tensor[4][5][6];
int i, j, k;

for (i = 0 ; i < 4 ; i++)
for (j = 0 ; j < 5 ; j++)
for (k = 0 ; k < 6 ; k++)
tensor[i][j][k] = 0;

- Contenido-