Ir al contenido principal

Array en C. Parte 2: Matrices...

Luego de explicar vectores en C, vamos a referirnos a otra implementación de los array: las matrices.

Una matriz podría definirse como un vector multidimensional, es decir un vector de dos o más dimensiones. En ellas se trabaja de la misma manera que con los vectores, sólo que en lugar de un solo índice tendremos tantos como dimensiones de nuestra matriz.

Declarando una matriz:

Para declarar una matriz, haremos exactamente lo mismo que en vectores (véase: Parte 1) sólo que agregaremos tantos índices como dimensiones queremos que tenga nuestra matriz.

Por ejemplo, si queremos declarar una matriz1 de enteros de 3 dimensiones 2x4x6 o una matriz2 de flotantes 3x3, lo haríamos como en la imagen.



Inicializando los elementos de una matriz:


Para inicializar los elementos de una matriz también lo haremos de la misma manera que en vectores.

*Podemos inicializarlas elemento a elemento usando los índices:


Aquí inicializamos el elemento [0][1] de nuestra matriz en 0 y el elemento [1][2] en 3.

Hay que recordar que los elementos que no inicialicemos en ningún valor contendrán basura.




*Podemos usar estructuras de control iterativas para inicializar los elementos de nuestra matriz:

Hay que tener en cuenta que necesitaremos tantas estructuras de control iterativas como dimensiones tenga nuestra matriz.




En el ejemplo anterior, inicializamos todos los valores de nuestra matriz en 0.

*O podemos inicializar los elementos de manera explícita:


Esta inicialización se puede hacer en una sola línea pero lo colocamos de esta manera para que vean que se inicializan por fila.





Usando scanf y printf con matrices:

Utilizaremos estas funciones de la misma manera que explicamos para vectores.

En el siguiente ejemplo en lugar de pedir uno por uno los elementos con scanf lo hacemos dentro de tantas estructuras iterativas como dimensiones tiene nuestra matriz:








Y hacemos lo mismo para mostrar los elementos con printf en este ejemplo:








Sólo que agregamos algunos espacios después de cada elemento y un salto de línea entre cada fila para que se vea en pantalla como una matriz. Si inicializáramos los 9 elementos de nuestra matriz 3x3 con los números del 1 al 9 con ese ejemplo obtendríamos algo así:

















 Disposición en memoria de los elementos de una matriz:


Para explicar este tema, tomaremos como ejemplo una matriz 3x3 (como la de nuestros ejemplos). Si pedimos prestada la definición al álgebra lineal la definición de matriz y la armamos tendríamos algo como esto:


Donde, si nos ponemos a recordar un poco o simplemente miramos el gráfico, los subíndices de cada elemento indican el primero la fila y el segundo la columna correspondiente.

En esta forma, si queremos por ejemplo el elemento a23 simplemente buscamos el elemento que se encuentra en la fila 2 y en la columna 3.

Hasta aquí podrán decir que no hay ninguna novedad y que incluso ya trabajamos en los ejemplos con este concepto de matriz y que hasta hicimos un printf que la mostraba en este formato ¿entonces para qué esta explicación?

La respuesta es que en C las direcciones de memoria no son multidimensionales ni mucho menos, sino que se encuentran una al lado de la otra. Entonces veamos lo que hace el compilador en estos casos.

Cuando se encuentra con la definición de una matriz, el compilador reserva tantas direcciones de memoria contiguas como se necesite para guardar todos los elementos de nuestra matriz. En el caso de la matriz de enteros 3x3 reservará el espacio necesario para guardar 9 enteros.

Dejando de lado la cantidad de memoria que ocupa cada tipo de dato (tema que explicaremos en una de las próximas entradas) y explicándolo de manera muy simple; el compilador reservará esas 9 posiciones contiguas, es decir una al lado de la otra con lo que nuestra matriz 3x3 en realidad sería algo como esto:



Para encontrar cada elemento a través de los 2 índices el compilador hará la operación ((i*3)+j) con lo que veamos qué obtendría en los 9 casos:



Si miramos bien obtuvimos las posiciones de nuestro esquema anterior.


En realidad a ese resultado el compilador lo multiplica por la cantidad de bytes que ocupa el tipo de dato correspondiente y ubica las posiciones a partir de la dirección de memoria de inicio de la matriz almacenada en su nombre, pero como ya dijimos esto lo veremos mejor en otra entrada.









Y llegamos al final de esta entrada, en la siguiente nos sumergiremos en las aguas de las cadenas de caracteres en C y veremos nuevas librerías para trabajar con ellas. Hasta la próxima.


Comentarios

Entradas populares de este blog

C: Conversiones de tipo (casting) en C...

El casting o simplemente cast  nos permite hacer una conversión explícita de un tipo de dato a otro, a criterio del programador siempre y cuando estos tipos sean compatibles. Este cast se realiza a través de un operador de conversión de tipos (type casting operator) y es un recurso a tener en cuenta ya que hay situaciones en que nos puede resultar de gran utilidad. Hacer uso de un cast es tan sencillo como poner (tipo de dato)  delante de la expresión o variable a convertir. Veamos un ejemplo: Declaramos una variable de tipo int con un identificador tan creativo como "a" y le realizamos diferentes cast a a para mostrarlo como si fuera un float, un double y un char en un printf. Lo que obtendríamos en pantalla sería lo siguiente: Donde tenemos el valor de nuestro a, a convertido en float y double (mostrándolo con 3 cifras decimales) y a convertido en char. Si vemos este último caso, al hacer la conversión de "a" a char toma a como el código ascii de

C: Ejemplos: Congruencia de Zeller (nivel básico) ...

La Congruencia de Zeller es un algoritmo que se atribuye al matemático alemán Julius Christian Johannes Zeller que vivió en el siglo XIX. Este algoritmo nos permite determinar el día de la semana que le corresponde a una fecha determinada del calendario Gregoriano. La fórmula que nosotros usaremos (con algunas modificaciones respecto de la original para poder usarla en  informática) es la siguiente: Donde h es el día de la semana (entre 0 y 6), J es año/100 (la centuria) y K es año mod 100 (el año de la centuria). Y hay que tener en cuenta que los meses de enero y febrero cuentan como el mes 13 y 14 del año anterior. Ahora que tenemos la fórmula, programemos el algoritmo en C mediante el uso de una función: Analicemos el código paso a paso: Tenemos en cuenta el caso de enero y febrero: Dijimos que estos meses corresponden a los meses 13 y 14 del año anterior por lo que los asignamos como corresponde (mes + 12 , que dará 13 para enero y 14 para febrero) y le rest

Algoritmos: Resolución de problemas y refinamientos en pseudocódigo...

En otras entradas, vimos las partes que debe tener nuestro algoritmo en pseudocódigo y las estructuras que utilizaremos para resolverlo. Ahora llega el turno de implementar todo en conjunto para dar origen a nuestra creación. Pero ¿cómo resolvemos un problema así? Para hacerlo, utilizaremos lo que llamamos refinamientos sucesivos. Este concepto consiste en dividir el problema en subproblemas más pequeños y a estos, a su vez, en otros más pequeños; y así sucesivamente hasta que la solución de los últimos sea trivial, sencillo de resolver. Luego usaremos todas las soluciones obtenidas para armar la solución de nuestro problema mayor. Este principio, tiene base en parte de la técnica divide and conquer (dependiendo de la traducción: "divide y vencerás") que es una de las muchas técnicas de resolución de algoritmos existentes. Como vemos, al dividir el problema en otros más pequeños y más fáciles de resolver, podemos pasar de un problema complicado a uno cuya solución es much