jueves, 8 de mayo de 2008

INICIALIZACION INDIRECTA DE MATRICES



La iniciación indirecta de matrices iniciada en el apartado anterior. En concreto, detallamos aquí el caso en que la definición no puede hacerse en una sola sentencia, lo que ocurre con las matrices que deben ser almacenadas en el montón, y cuyas dimensiones no se conocen en el momento de la declaración.Hemos señalado que en estos casos, se define un puntero del tipo adecuado. Por ejemplo:char** a; // Ok. puede señalar a matriz de dos dimensiones!A continuación hay que reservar el espacio de almacenamiento, y finalmente, inicializar los miembros de forma individualizada. Para esto se requiere un poco de práctica y conocer ciertos trucos (cuyas claves se han apuntado antes) para no perderse en el proceso:· El identificador de una matriz se describe como un puntero. En particular, el identificador de matrices bidimensionales cuyo espacio de almacenamiento se localice en el montón, se describe como puntero-a-puntero (lo mismo para n dimensiones . · Los elementos se almacenan por filas . Esta frase encierra el secreto de crear y destruir matrices multidimensionales sin necesidad de "verlas", dado que las de más de tres dimensiones tienen una difícil representación mental. Como veremos en los ejemplos, el proceso para crear una matriz de n dimensiones (x1, x2, ... xn) empieza por crear la primera fila; una matriz de una dimensión con x1 elementos. §2 La creación de matrices de una dimensión ya se trató en el epígrafe dedicado al almacenamiento de matrices ( ; el razonamiento puede hacerse extensivo a matrices de n dimensiones. Por ejemplo, consideremos declarar matrices de tres dimensiones:void func() { int a1[2][4][3]; // L1: objeto de duración automática static int a2[2][4][3]; // L2: objeto persistente (estático)}En L1 tenemos un objeto a1 de duración automática alojado en la pila; en L2 tenemos un objeto estático a2 alojado en el segmento. Utilizaremos ahora el procedimiento de "C++ clásico" para crear una matriz ai de tres dimensiones en el montón. En primer lugar, recordaremos que desde ambas ópticas (puntero ↔ matriz), se tiene:ai: Puntero-a-puntero-a-puntero-a-int ↔ matriz de una fila de dos elementos (la llamaremos bi), cada uno de los cuales es una matriz. Traducido a código:int*** ai = (int***) calloc(2, sizeof(int**)); // I:Traducido a lenguaje coloquial: ai es una variable automática (4 bytes en la pila) de tipo puntero-a-puntero-a-puntero-a-int, señalando un espacio bi (8 bytes en el montón) capaz para albergar dos punteros-a-puntero-a-int.La situación se ha esquematizado en la figura 1.bi: Puntero-a-puntero-a-int ↔ matriz de una fila de cuatro elementos, cada uno de ellos es una matriz (la llamaremos ci) de una dimensión. Traducido a código:int** bi = (int**) calloc(4, sizeof(int*)); // II:Coloquialmente: bi es una variable automática (4 bytes en la pila) de tipo puntero-a-puntero-a-int, señalando un espacio ci de 16 bytes en el montón, que es capaz para albergar 4 punteros-a-int.ci: Puntero-a-int ↔ matriz de una fila de tres elementos, cada uno de los cuales: es un int. Traducido a código:int* ci = (int*) calloc(3, sizeof(int)); // III:En lenguaje coloquial: ci es una variable automática (4 bytes en la pila) de tipo puntero-a-int, señalando un espacio ci de 12 bytes en el montón, capaz para albergar 3 int.§2.1 Poniendo las sentencias anteriores en forma de una secuencia ejecutable, resultaría:int*** ai = (int***) calloc(2, sizeof(int**)); // L1:int i, j;for (i = 0; i < j =" 0;" 8 =" 24">EjemplosComo caso concreto, se muestra la definición indirecta de una matriz de dos dimensiones matriz[m][n] en el montón. Se ha previsto para tres filas y cinco columnas matriz[3][5]; sus elementos son del tipo long double, pero puede ser adaptada fácilmente para aceptar entradas de usuario en tiempo de ejecución, de forma que se acepten otros tamaños y/u otros tipos.Se presenta en dos versiones: la primera utiliza las funciones de librería clásicas de asignación y liberación de espacio (calloc y free). La segunda más moderna , utiliza las nuevas funciones de librería (new y delete). Finalmente, a título comparativo, se incluye una tercera versión que supone la definición directa utilizando el nuevo operador new[] para matrices .§3.1 Versión C clásicoEl proceso es análogo al esquematizado en la figura anterior aunque más sencillo (carece de la tercera fase), ya que la matriz a crear es de dos dimensiones. Consistirá en la creación de los siguientes objetos:· En el montón: o 3 matrices de 5 long double cada una, creadas en el bucle M4 (contienen los "datos" de la matriz). o 1 matriz 3 punteros (creada en M3). Cada uno de sus miembros señala al primer elemento de una de las matrices anteriores (la asignación se realiza en M5), por lo que son de tipo long double*. · En la pila: o Un puntero que señala al primer miembro de la matriz anterior, por lo que su tipo es puntero-a-puntero-a-long double (long double**). #include // Versión C clásico#include // para free & calloctypedef long double TIPO; // L3:typedef TIPO** OBJETO;unsigned int fil = 3, col = 5; // L5:void des_asigna(OBJETO); // L6: prototipoint main(void) { // =============== unsigned int i, j; OBJETO matriz; // M2: matriz = (OBJETO) calloc(fil, sizeof(TIPO*)); for (i = 0; i < i =" 0;" j =" 0;" i =" 0;" j =" 0;" i =" 0;">.En M5 se reserva memoria para contener col objetos tipo TIPO (long double). El proceso se realiza 3 veces, una para cada fila. Al final del bucle M4 se han reservado 3 espacios para 5 long double cada uno. Las direcciones de inicio de estos bloques se asignan a cada miembro de la matriz creada en M3.Podríamos considerar que M5 crea tres matrices de 5 elementos cada una. Este conjunto de 15 elementos constituiría la seudo-matriz m[3][5] cuyos miembros se inician con valores arbitrarios en el bucle M7.El bucle M11 es análogo al anterior (un bucle de 5 iteraciones anidado en otro de 3), y se encarga de mostrar los valores asignados.M16 invoca a la función encargada de rehusar el espacio previamente asignado en M3 y M5. Observe que el proceso se realiza en orden inverso al de asignación. El bucle F2 realiza la tarea inversa a la que se realizó en M4. F3 desasigna los espacios asignados en M5. A continuación F4 deshace la asignación realizada en M3.Finalmente el objeto matriz y el resto de objetos automáticos serán destruidos en M17 al salir de main.