Vous êtes sur la page 1sur 17

Arreglos bidimensionales (matrices) octubre 1, 2007 Es un arreglo de dos dimensiones.

Son estructuras de datos que agrupan muchos datos del mismo tipo, en donde cada elemento se puede trabajar individualmente y se puede referenciar con un mismo nombre. Se usan para representar datos que pueden verse como una tabla con filas y columnas. Declaracin: Tipo_dato nombre_matriz [ndice fila] [ndice columna] Uso: Nombre_matriz [subndice1] [subndice2] int matriz [2][2] char mapa [100][100] int certamen [60][4] Declara una matriz de 3 filas por 4 columnas: int matriz [3][4]; Declaracin e iniciacin: int matriz [2][2]={1,2,3,4} 1 2

Para referenciar un elemento de la matriz, debe darse un nombre de la matriz y el ndice de la fila y de la columna que el elemento ocupa en dicha matriz. Es importante que los ndices de las matrices tanto de las filas como de las columnas empiezan en 0 y terminan en tamao fila-1 y tamao columna-1 respectivamente. A las matrices se le asignan automticamente valores iniciales predeterminados a cada uno de sus elementos, de acuerdo a los siguientes criterios:

Si el tipo del arreglo es numrico, a sus elementos se les asigna el valor cero. Si el tipo del arreglo es char, a sus elementos se les asigna el valor \u0000. Si el tipo del arreglo es bool, a sus elementos se les asigna el valor false. Si el tipo del arreglo es una clase, a sus elementos se les asigna el valor null.

Ejemplo: #include <stdio.h>

int main() { int fila, columna; int matriz[2][2]; for(fila=0; fila<2; fila++) for(columna=0; columna<2; columna++) printf(%d, matriz*fila+*columna+); return 0; }

l lenguaje C permite el uso de matrices, es decir, arrays bidimensionales. En general, todo lo que vamos a estudiar en esta seccin se puede extrapolar a arrays incluso n-dimensionales (o tensores), aunque en la prctica el uso de arrays con ms de 2 dimensiones no es muy comn. La declaracin de una matriz o array bidimensional es: tipo variable_matriz[N][M]; Donde N y M son el nmero de filas y de columnas respectivamente (la dimensin de la matriz). Se ha escrito la dimensin con letras maysculas, ya que deben ser constantes, y al igual que con vectores se suelen definir con constantes, por ejemplo: #define N 4 //nmero de filas de las matrices que voy a declarar #define M 5 //nmero 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 sera la forma y los elementos de una matriz a[4][5], de tamao 45.

Vase que el primer elemento del array bidimensional es el a[0][0], el siguiente sera el a[0][1], y as, hasta llegar al elemento a[3][4]. Matriz bidimensional ndice de columna Elemento en 0 0 1 ndice de fila 2 3 a[2][0] a[3][0] a[2][1] a[3][1] a[2][2] a[3][2] a[2][3] a[3][3] a[0][4] a[0][4] a[0][0] a[1][0] 1 a[0][1] a[1][1] 2 a[0][2] a[1][2] 3 a[0][3] a[1][3] 4 a[0][4] 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 estara almacenada en la memoria la matriz anterior a[4][5]. Hay que aclarar que algunos lenguajes de programacin (FORTRAN) utilizan el convenio contrario, es decir, almacenan las matrices por columnas. Distribucin de una matriz bidimensional en memoria Fila 0 1 2 3

En a[0] a[0] a[0] a[0] a[1] a[1] a[1] a[1] a[2] a[2] a[2] a[2] a[3] a[3] a[3] a[3] mem [0] [1] [2] [3] [0] [1] [2] [3] [0] [1] [2] [3] [0] [1] [2] [3] oria 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 continuacin se muestra un sencillo algoritmo que nos permite recorrer por filas completamente matrices (ya que el bucle interno acta sobre los ndices de las columnas); en primer lugar se valoran las matrices b y c con ciertos nmeros y seguidamente se realiza la operacin matricial suma (en matemticas se define un operador suma matricial con el mismo smbolo '+', de forma que este algoritmo sera 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 nmeros. for (i = 0 ; i < 4 ; i++) for (j = 0 ; j < 5 ; j++) { b[i][j] = i; c[i][j] = (i * j) + j; } // A continuacin 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] ; } Adems 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 matemticas A=BC), ya que en tal operacin es necesario que una fila de B se multiplique por una columna de C (en los ejercicios de este apartado se estudia esta operacin). Hay que tener en cuenta que, por un lado, la declaracin 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 ejecucin considerable. Por ejemplo si es pretende trabajar con una matriz donde se almacena un elemento tipo double por cada pxel de la pantalla de alta resolucin, esto supone declarar una matriz de dimensin [1024][768], es decir, un consumo de 81024768=6,291,456 bytes, o sea, ms de 6 MB. Posteriormente cada vez que se ejecute un bucle anidado para recorrer tal matriz, se ejecutarn 1024768=786,432 iteraciones! Por ltimo y como explicamos antes, se puede extrapolar todo lo anterior a arrays ndimensionales. As, el siguiente ejemplo declara un tensor de dimensin 456 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; La utilizacin de punteros en C es bsica para la programacin avanzada y ms eficiente. Aunque en este texto no vamos a poder usar algoritmos con punteros en toda su potencialidad, al menos no queremos dejar sin explicar el concepto bsico y algunas de las utilidades que sin ellos no podran realizarse, como por ejemplo:

Que una funcin modifique o escriba en los parmetros que se le enva. Pasar a una funcin un vector (o matriz) completo como parmetro.

Para entender el concepto de puntero, hay que recordar un poco algunos conceptos de los lenguajes de bajo nivel; los que se usan en los procesadores (CPU). Recordemos que precisamente el lenguaje C es un lenguaje de alto nivel con algunos aspectos de bajo nivel, de ah su potencia y popularidad. En concreto, hay que repasar el concepto de direccin y dato de memoria que estudiamos en el captulo 2. Recordemos que, al final, tras todas las traducciones necesarias para que nuestro cdigo C se ejecute, la CPU no va a trabajar con variables abstractas, sino con direcciones de acceso a memoria (en cada una de las cuales habr un dato). As la siguiente asignacin: a=b; se traducir en cdigo mquina, por algo que ejecutar algo como las dos siguientes instrucciones mquina: CARGA REGISTRO, DIR(variable b) ESCRIBE DIR(variable a), REGISTRO Es decir, se accede primero a memoria para leer el dato de la direccin de la variable b, que se almacena en un registro de la CPU, y despus se accede a memoria para escribir el dato de tal registro en la direccin de la variable a. Este sencillo ejemplo es extensible para todas las variables que hemos estado usando, ya sean double, vectores, matrices, cadenas, etc. Por eso, en general todas las variables no son ms que direcciones de memoria, y es el compilador el que realiza el "trabajo sucio" de asignar a cada variable la direccin de memoria que considere ms oportuna. Pues bien, un puntero es una variable cuyo valor es precisamente una direccin de memoria. Esa es precisamente la clave por la cual un programa escrito usando punteros suele ser ms rpido (eficiente) que otro que no los usa: usar punteros, es decir, direcciones, evita muchas instrucciones que el compilador necesita introducir a veces para calcular las direcciones de las variables. Pero, claro est, si se inventaron los compiladores para evitar el arduo trabajo de conocer o traducir las variables a sus direcciones, no tendra sentido tener que trabajar en un lenguaje de alto nivel como C con direcciones. Por eso, en realidad, las direcciones que van a contener los punteros se van a usar, pero no tienen por qu conocerse nunca. Eso s, el puntero debe tener una direccin til, es decir, la direccin de otra variable de memoria. Para comprender mejor este concepto, en la siguiente Figura 4.5 se han representado los bytes de memoria, cuyas

direcciones son la 0xF000F100, 0xF000F101, etc.(es decir, direcciones de 32 bits, como es habitual en los ordenadores actuales). Supongamos que una variable a tipo char (es decir, ocupa un byte) contiene la letra 'W', y va a ser colocada por el compilador en la direccin 0xF000F107. Por otro lado, una variable puntero p es colocada por el compilador en la direccin 0xF000F100. Si queremos que el puntero contenga la direccin de a, entonces a partir del byte de la direccin 0xF000F100 debe estar escrito el valor de la direccin de a: 0xF000F107. Este valor ocupa 4 bytes, que sern: 0xF0, 0x00, 0xF1, 0x07, los cuales se observan en la Figura 4.5. Mapa de memoria Direccin de memoria 0xF000F100 0xF000F101 0xF000F102 0xF000F103 0xF000F104 0xF000F105 0xF000F106 0xF000F107 0xF000F108 0xF000F109 Figura 4.5 Se dice que el valor de un puntero "apunta" a una variable (en el ejemplo anterior, "p apunta a a", de ah que la flecha de la figura salga de p y apunte a la variable a). Tambin se suele expresar como que "el contenido de p es a". Notemos finalmente que el tamao de un puntero depende del tamao de una direccin, en nuestro caso 32 bits, pero no de lo que apunta el puntero. Es decir, si la variable puntero p apuntase a otra variable x tipo double, aunque x tendra un tamao de 8 bytes, el puntero seguira ocupando 4 bytes, es decir 32 bits de direccin. La manera formal de declarar en C un puntero es la siguiente: tipo *nombre_variable Dato en memoria 0xF0 0x00 0xF1 0x07 irrelevante irrelevante irrelevante 'W' irrelevante irrelevante irrelevante irrelevante irrelevante a irrelevante irrelevante puntero p; apunta a la variable a Variable abstracta

Donde tipo es cualquier tipo vlido de C y nombre_variable es el nombre de la variable puntero. El smbolo asterisco indicar al compilador que dicha variable es un puntero. Existen dos operadores especiales unarios (slo necesitan un operando) para el manejo de los punteros: & y *. El operador & devuelve la direccin de memoria de una variable, de ah que se llame operador de direccin. El operador *, llamado por el contrario de "indireccin", devuelve el valor de la variable a la que apunta. En ele ejemplo anterior si se quiere que "p apunte a a", ha de hacerse: p=&a, es decir, la direccin de a la introducimos en p. Tras esto, si se quiere saber "el contenido de p" puede recurrirse a: *p, que nos dara el valor de a, o sea 'W'. Sigamos viendo con otro ejemplo cual sera, intuitivamente, el funcionamiento de estos operadores. Supongamos que tenemos cuatro variables definidas, las dos primeras como variables enteras, a las cuales llamaremos a y b, y las otras, punteros a variable entera: p1 y p2. En general, es una buena costumbre para empezar a trabajar con punteros, nombrarlos empezando por la letra 'p', para distinguir claramente las variables que son punteros de las que son datos. La manera de declarar estas variables en C sera: int a, b; int *p1, *p2; O bien, todo junto: int a, b, *p1, *p2; Supongamos que el compilador ha ubicado las variables en memoria como muestra la Figura 4.6 (tenga en cuenta que un entero ocupa 32 bits, 4 bytes, lo mismo que un puntero, supuestas las direcciones de 32 bits). Tras la declaracin de las cuatro variables, ninguna tiene un valor definido: a y b contendrn un valor indefinido y los punteros p1 y p2, no apuntarn a ningn sitio, como se muestra en esta tabla: Mapa de memoria Direccin de memoria 0xF000F100 0xF000F104 0xF000F108 0xF000F10B Dato en memoria Valor indefinido Valor indefinido Direccin indefinida Direccin indefinida Variable abstracta a b p1 p2

Para empezar a trabajar con las variables, asignamos un valor a cada una: a = 3; p1 = &a;

p2 = &b;

b = (*p1) + 5; El resultado que obtendremos es el que muestra en esta tabla: Mapa de memoria Direccin de memoria 0xF000F100 0xF000F104 0xF000F108 0xF000F10B Dato en memoria 3 8 0xF000F100 0xF000F104 Variable abstracta a b p1 p2

Qu ha ocurrido? Evidentemente, a contiene un 3, p1 contiene la direccin de a (0xF000F100), y p2 la de b (0xF000F104), debido a las tres primeras lneas de cdigo. Pero la cuarta lnea ha valorado la variable b con "lo que apunta p1" ms 5. Como "lo que apunta p1" es a, y a vale 3, el resultado es que b tendr un 8. Obsrvese que, aunque en el ejemplo se muestran las direcciones para aclarar los conceptos, stas no van a aparecer nunca en el cdigo C, tal como dijimos antes. Por otro lado, conviene resaltar para fijar ideas que la direccin de cualquier variable es siempre constante (es decir, &a , &b son constantes), mientras que el contenido de un puntero (*p1, *p2) es variable. Pero y si hacemos despus lo siguiente:? (*p2) = (*p1) + 100; Aqu hemos valorado de forma "indirecta" a la variable b, puesto que p2 apunta a b, y la expresin (*p2) a la izquierda de la asignacin, conduce a asignar un valor a b. Por tanto, lo anterior es lo mismo que escribir: b = a + 100; Naturalmente la ltima lnea que hemos escrito es ms sencilla y comprensible que la penltima, y por tanto, esta forma de usar no es realmente til, pero es la mejor manera de comprender los punteros, para usarlos luego cuando realmente sea interesante. A partir de ahora evitaremos el uso de parntesis en los accesos a los contenidos de los punteros como *p2, puesto que el operador de indireccin * tiene una prioridad muy alta (ver apartado 4.2) y no se requieren ni son usuales tales parntesis. Aclaremos a continuacin que varios punteros pueden apuntar a la misma variable, de forma que la modificacin del "contenido de uno de los punteros", implicara la modificacin

automtica del contenido del resto de punteros (y, por supuesto, de la variable). Esta caracterstica es la que nos permitir poder modificar los parmetros de llamada de una funcin, y que esta modificacin permanezca cuando salgamos de dicha funcin (esto se estudiar dentro del paso de parmetros a funciones). Fijmonos en el siguiente trozo de cdigo: int a=130; int *p1, *p2, *p3;

p1 = &a; p2 = p1; //p2=&a hara lo mismo p3 = p2; //p3=&a hara lo mismo Las siguiente Figura 4.8 muestra la memoria, suponiendo que las variables empiezan a colocarse a partir de la direccin de memoria 0xF000F100. Mapa de memoria Direccin de memoria 0xF000F100 0xF000F104 0xF000F108 0xF000F10B Dato en memoria 130 0xF000F100 0xF000F100 0xF000F100 Variable abstracta a p1 p2 p3

Por lo tanto, cualquier cambio en el valor de a, o del contenido de los punteros, implicara un cambio en todas las variables. As, *p3 = 50, es idntico que a=50 o *p1=50, y en cualquier caso automticamente tendremos que a valdr igual que *p1, *p2 o *p3, o sea, 50. Por ltimo en este apartado hemos de explicar un "puntero especial" que siempre existe cuando se declara un vector (o matriz): se trata del propio nombre del vector. En efecto, la declaracin de un vector como: double vect[100]; implica que, mientras que cada una de las componentes vect[0], vect[1], etc. es un double, la expresin vect sera un puntero a double, que, como es evidente apunta al propio vector. En concreto la expresin vect no es una variable, sino simplemente la direccin (una constante) del vector. De esa forma, tiene sentido modificar un puntero declarado como tal pero no intentar modificar la direccin de vect:

double vect[100], d; double *pd1, *pd2;

pd1 = vect; // pd1 tambin apunta ahora al vector pd2 = &d; // modificacin del puntero pd2 vect = pd2; // error de compilacin, vect es una constante El uso del nombre de un vector como direccin o puntero constante, es vital para el paso de vectores como parmetros, lo cual estudiaremos a continuacin. Como ya se indic en el apartado anterior una de las grandes utilidades del uso de punteros es el hecho de que una funcin pueda modificar o escribir en los parmetros que se le enva. Hasta ahora, las funciones reciban parmetros pero solo consultaban sus valores para realizar clculos u operaciones internas. Con los punteros ahora adems podrn alterar las variables a las cuales hacen referencia esos argumentos de tipo puntero. Veamos un ejemplo de esto en el siguiente problema: void main(void) { int numero, *p_numero; void convertir_a_siguiente_numero_primo (int *); ... numero = 5; p_numero = &numero; ... // La variable numero contiene un 5 convertir_a_siguiente_numero_primo (p_numero); // Llamada a funcin // La variable numero contiene un 7 ... }

void convertir_a_siguiente_numero_primo (int *puntero) {

... *puntero = 7; } En el ejemplo anterior se tiene un programa principal en el que se define un entero y un puntero a un entero. Tambin existe una funcin llamada convertir_a_siguiente_numero_primo que tiene a un puntero a entero como parmetro. Esta funcin lo que hace es dejar en la variable entera apuntada por el apuntador el siguiente nmero primo al que actualmente hay contenido. Inicialmente la variable numero tiene un 5. A la funcin convertir_a_siguiente_numero_primo se la pasa el puntero p_numero que apunta a numero. En el cuerpo de la funcin el contenido de la variable numero es modificado gracias al puntero p_numero y despus de la llamada a la funcin contiene un 7. La llamada a la funcin tambin podra haberse hecho de la siguiente manera en el ejemplo anterior siendo el resultado el mismo: convertir_a_siguiente_numero_primo (&numero); El paso de parmetros a funciones mediante punteros no se limita tan slo a los tipos de datos simples. Hasta el momento, hemos trabajado con funciones que tenan como parmetros variables simples pero nunca vectores o matrices. Esto se deba a que C no podemos pasar un array o vector completo como parmetro de una funcin. Sin embargo, si podemos pasar un puntero que apunta a un array. Para ello bastar con pasar como parmetro el nombre del array sin ndices, como se acaba de explicar en la seccin precedente. Veamos un ejemplo: void main(void) { void funcion1(int *); //declaracin int a[10], b[50]; ... // A continuacin paso como parmetro el nombre de los arrays: funcion1 ( a ); //llamada 1; a[0] y a[1] valdrn lo mismo tras esto funcion1 ( b ); //llamada 2; b[0] y b[1] valdrn lo mismo tras esto } La funcin no devuelve ningn valor y contiene un parmetro que va a ser un puntero a enteros. La funcin deber estar definida por ejemplo as: void funcion1(int *pvector) //definicin /*esta funcin iguala el primer elemento del array pasado como parmetro al segundo */

{ /*el puntero pvector (variable local de funcion1) adquirir en la llamada 1 la direccin del array a de la funcin main(), pero en la llamada 2 la direccin de b */ pvector [0] = pvector [1] ; } Como vemos, dentro de la funcin se puede usar el array pvector[] como otro array ms, teniendo siempre en mente que en la llamada 1, se pas el array a[] y por tanto el uso de pvector[0], y de pvector[0] se refieren a a[0], a[1], mientras que en la segunda llamada pvector[0], y de pvector[0] se referirn a b[0], b[1]. El uso del puntero pvector como un array es ms evidente si la definicin y la declaracin de la funcin se hacen de la siguiente manera, tambin vlidas: void main(void) { void funcion1(int []); //declaracin ... }

void funcion1(int pvector[]) { pvector [0] = pvector [1] ; } Qu est ocurriendo realmente al ejecutar esta funcin que usa el puntero pvector? Pues que la funcin main() pasa como valor del parmetro la direccin del comienzo de un array (ya sea a[] o b[]), que es recogida por la funcion1() en su parmetro pvector. De esa forma, el array con que trabaja funcion1() (o sea, pvector[0], pvector[1]) es idntico al array a[] del main() en la primera llamada (como si en main() se escribiera a[0]=a[1]), e idntico a b[] en la segunda llamada (como si en main() se escribiera b[0]=b[1]). En este tipo de acciones con vectores pasados por parmetro, hemos de advertir de nuevo que el compilador no va a comprobar los lmites de los vectores. Es decir, no se introduce en el ejecutable ningn mecanismo de deteccin de si el ndice usado en la funcin est por debajo del 0 o de si excede el valor de la dimensin del vector. Por ejemplo, si la funcion1() hubiera usado en la primera llamada cualquiera de los elementos pvector[-1], pvector[10], o en la segunda llamada pvector[-1] o pvector[50], (lo que significara el acceso a los elementos a[-1], a[10], b[-1], b[50], respectivamente) entonces el cdigo dara resultados imprevisibles. En

efecto, los accesos a las componentes [-1] o a las que sobrepasan la dimensin de los vectores a[] y b[], leern o escribirn en posiciones de memoria que no estn reservada a tales vectores, las cuales, probablemente contengan el valor de otras variables diferentes. Este hecho producira con casi toda seguridad un mal funcionamiento del programa ejecutado (o una interrupcin del mismo por parte del sistema operativo, debido a esta "violacin" del acceso a la memoria si ste detecta que el programa ha accedido a componentes no reservadas previamente). Luego debemos de tener en cuenta que es misin del programador, asegurar que el array pasado como parmetro a una funcin (puntero pvector) se maneja dentro de los lmites fijados en la declaracin de los vectores originales (a, b). Como ya dijimos, aunque esto parezca un inconveniente respecto a otros lenguajes que s introducen instrucciones de deteccin de violaciones de acceso, es una ventaja en cuanto que la ausencia de tales instrucciones de deteccin acelerar la ejecucin de los programas. Lo habitual cuando una funcin ha de manejar un puntero que apuntar a vectores de diferente nmero de elementos es enviar otro parmetro que informe del nmero de elementos que contiene el vector original (a, b del main() en nuestro ejemplo). Como ejemplo de esto ltimo vamos a escribir y utilizar la funcin anula() que pone a cero todas las componentes de un array cualquiera. A esta funcin le pasaremos como parmetro tanto el puntero al array como el nmero de elementos del mismo. Dentro de la misma se realizar un bucle desde la componente 0 a la ltima, para que ponga a cero los elementos, es decir: void anula (int pvector[], int num_elem) { int i;

for (i=0 ; i< num_elem ; i++) pvector[i] = 0; return ; } Ahora lo importante es tener cuidado en el uso de esta funcin, es decir que el parmetro que pasa el nmero de elementos, nunca exceda la dimensin del vector original. Por ejemplo, para anular las componentes de a[] ha de pasarse 10, y para anular las de b[] podemos pasar un 50: void main(void) { void anula (int *, int); //declaracin

int a[10], b[50]; ... // A continuacin paso el nombre de los arrays y su nmero de elementos anula ( a, 10 ); //llamada 1; anula las 10 componentes de a[] anula ( b, 50 ); //llamada 2; anula las 50 componentes de b[] } Como hemos explicado antes, un error grave ocurrir si en el uso de la funcin anula() se comete un error y se enva un nmero de elementos mayor del que disponen los arrays. Por ejemplo, si nos equivocamos y alteramos los nmeros 10 y 50, el resultado de la primera llamada podra ser catastrfico: Notemos finalmente que nuestra funcin anula() es genrica y puede anular un nmero de componentes inferior a la dimensin total de los arrays (bastara con indicarlo en el segundo parmetro, como en anula(b, 10), que slo anular 10 elementos de b[]). Por ltimo, podemos trabajar de forma similar con paso de matrices a funciones. Cuando el vector a pasar es bidimensional tendremos que tener cuidado con la definicin de la funcin, puesto que el compilador debe conocer exactamente, cual es el tamao de la segunda dimensin. Vemos un ejemplo. void main(void) { int b[6][5]; void funcion2(int [][5]); //la segunda dimensin hay que drsela ... funcion2 ( b ); // llamada: paso la matriz como puntero ... } La funcin deber estar definida con el tamao de la segunda dimensin: void funcion2(int pmatriz[][5]) { pmatriz [0][0]= pmatriz [1][1]; // pmatriz[0][5] no es vlido, accede a una columna que no existe // pmatriz[6][4] no es vlido si la matriz original es b[6][5]

// se accede a una fila que no existe ... } Por otra parte, como mencionamos en los arrays unidimensionales, se han construido libreras estndar de tratamiento de cadenas que copian, comparan, etc. cadenas de caracteres. Los parmetros de entrada son cadenas de caracteres, que se suponen que acaban en el carcter especial de fin de cadena (ASCII 0x0). Por tanto estas funciones trabajan o iteran con los elementos de las cadenas hasta que se topan con tal carcter de fin de cadena. A continuacin se explican algunas de las ms comunes: gets (char *cadena) Lee de teclado una cadena de caracteres y la introduce a partir de la direccin dada por 'cadena'. puts (char *cadena) Imprime en pantalla la cadena de caracteres que viene a partir de la direccin dada por 'cadena'. strlen (char *cadena) Devuelve la longitud de la cadena de caracteres que viene a partir de la direccin dada por 'cadena'. Devuelve la longitud de la cadena. strcat (char *cadena1, char *cadena2) Concatena la cadena de caracteres dada por 'cadena1' con la 'cadena2', es decir, aade al final de 'cadena1' la 'cadena2'. strcmp (char *cadena1, char *cadena2) Compara la cadena de caracteres dada por 'cadena1' con la 'cadena2'. strcpy (char *cadena1, char *cadena2) Copia la cadena de caracteres dada por 'cadena1' en la 'cadena2'. Como ejemplo de construccin de una de estas funciones escribimos una posible implementacin o definicin de una de ellas: int strlen(char *cad) { int i=0; while (cad[i] != 0) i++;

return (i); } Desde un punto de vista didctico, se recomienda como ejercicio construir o implementar las funciones anteriores de cadenas. Sin embargo, en la prctica, si se ha de trabajar con cadenas, recomendamos que, cuando sea posible, se usen las funciones de las libreras en vez de las implementaciones "caseras" o particulares, puesto que las funciones de las libreras suelen estar muy optimizadas y ser mucho ms rpidas que las traducciones que realizan los compiladores de nuestro cdigo C. El lenguaje C nos va a permitir inicializar los arrays, en el momento de declararlos. La sintaxis es la siguiente: tipo variable_array[tamao] = {lista de valores separados por comas}; Un ejemplo podra ser: int a[5] = {0, 1, 2, 3, 4}; Que introducira en el vector a[] lo mismo que el siguiente bucle: for (i=0 , i<5; i++) a[i] = i; La diferencia est en que el bucle debe ejecutarse, mientras que la inicializacin se produce en el momento de declarar las variables. Cuando el array tiene ms de una dimensin, su inicializacin es idntica, teniendo en cuenta el tamao de la matriz. Un ejemplo podra ser el siguiente: int a[3][4] ={ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; //3x4=12 elementos int b[2][3] = { 0, 1, 2, 3, 4, 5 }; //2x3=6 elementos Ntese que el nico separador en el ejemplo anterior son las comas, y que los saltos de lnea no son necesarios ponerlos para realizar una correcta inicializacin (el compilador no los tiene en cuenta, los ignora). La fila 0 de a contendr los valores 1, 2, 3, 4; la fila 1 contendr 5, 6, 7, 8, y el resto para la fila 2. A pesar de la posibilidad de inicializar matrices y vectores, cuando la dimensin de stas es grande sera muy engorroso escribir una lista de nmeros separados por comas, y se suele recurrir a bucles. Por ejemplo, observe que el siguiente bucle asigna a la matriz b[] el mismo valor que la inicializacin anterior:

for (i=0 , i<2; i++) for (j=0 , j<3; j++) a[i][j] = i*3 + j; O bien el siguiente bucle: contador=0; for (i=0 , i<2; i++) for (j=0 , j<3; j++) a[i][j] = contador++;

Vous aimerez peut-être aussi