• No se han encontrado resultados

Consideraciones finales con arreglos

5. Recursividad

6.4. Consideraciones finales con arreglos

Probablemente en la Secci´on 6.2.3 se haya preguntado, por qu´e la funci´on suma del Ejemplo 6.8 recibe tambi´en como par´ametro a la matriz resultado c, ¿acaso no ser´ıa m´as conveniente y de mejor dise˜no regresar dicha matriz como valor de retorno de la funci´on?.

Considere el Ejemplo 6.10, el cual propone, en base a los conceptos has- ta ahora discutidos, una posible soluci´on al planteamiento de la pregunta anterior.

La l´ınea 8 presenta una forma intuitiva en la que se expresar´ıa el deseo de que la funci´on regrese una matriz; note que la lista de par´ametros se ha reducido en uno respecto de la funci´on suma del Ejemplo 6.8. T´omese el tiempo necesario para comparar ambas funciones antes de continuar.

Con la excepci´on del cambio expresado en el p´arrafo anterior, las funciones de los Ejemplos 6.8 y 6.10 son esencialmente la misma. El Ejemplo 6.10 no

6.4. CONSIDERACIONES FINALES CON ARREGLOS 135

compila, ya que no es posible indicarle al compilador de C que regrese una matriz como el tipo de dato de retorno para una funci´on.

1 /∗ Ejemplo de f u n c i o n que i n t e n t a r e g r e s a r una m a t r i z como v a l o r 2 de r e t o r n o p a r a l a f u n c i o n . 3 @autor R i c a r d o Ruiz R o d r i g u e z 4 ∗/ 5 #d e f i n e M 10 6 #d e f i n e N 10 7 8 f l o a t [ ] [ N ] suma ( f l o a t a [ ] [ N ] , f l o a t b [ ] [ N ] , i n t m, i n t n ) { 9 f l o a t c [M] [ N ] ; 10 i n t i , j ; 11 12 f o r ( i = 0 ; i < m; i ++) 13 f o r ( j = 0 ; j < n ; j ++) 14 c [ i ] [ j ] = a [ i ] [ j ] + b [ i ] [ j ] ; 15 16 return c ; 17 }

Ejemplo 6.10: Matriz como valor de retorno

¿Por qu´e no se puede indicarle al compilador de C que regrese un arre- glo?, adem´as de que la gram´atica del lenguaje C no considera v´alida dicha expresi´on, se debe tambi´en a que las variables declaradas dentro de una fun- ci´on son variables autom´aticas y locales a ella, por lo tanto, s´olo existen en el contexto de la propia funci´on, por lo que cuando ´esta termina, los datos son desechados antes de regresar el control de ejecuci´on a la siguiente sentencia despu´es de la invocaci´on de la funci´on, por lo que no tendr´ıa sentido regre- sar como valor de retorno una variable que ya fue desechada. Por otro lado, a´un cuando dicha variable fuera afectada por el modificador static (v´ease la Secci´on 4.3), la variable s´olo ser´ıa accesible dentro del contexto de la funci´on. Finalmente, la respuesta a la pregunta expresada al inicio de la secci´on es, que en general ser´ıa m´as conveniente y tambi´en un mejor dise˜no el enfoque del Ejemplo 6.10, pero en el lenguaje de programaci´on C, los arreglos no se pueden regresar como valor de retorno de funci´on, al menos no con los arreglos declarados en tiempo de compilaci´on y con las herramientas hasta ahora estudiadas, hace falta algo m´as, y ese algo m´as son los apuntadores, los cuales se estudiar´an en el Cap´ıtulo 7.

136 CAP´ITULO 6. ARREGLOS

6.5.

Ejercicios

1. Modifique el Ejemplo 6.1 de tal forma que imprima los 15 elementos del arreglo arreglo antes del ciclo for de la l´ınea 13, con la finalidad de verificar que los elementos no inicializados expl´ıcitamente, son impl´ıci- tamente inicializados a 0, tal y como se describe en el texto.

2. Para el Ejemplo 6.1 suponga que N es 5 (l´ınea 7) y que los inicializa- dores (l´ınea 10) son:

{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

¿Qu´e sucede si existen m´as inicializadores que n´umero de elementos pa- ra el arreglo?. Haga un programa que compruebe su respuesta.

3. Escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void rellenaArreglo(int a[ ], int n, int rango)

La funci´on deber´a inicializar los n elementos del arreglo a, con n´umeros aleatorios definidos entre 0 y rango - 1.

4. Escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void inicializaArreglo(int a[ ], int n, int valor) La funci´on deber´a inicializar los n elementos del arreglo a, con el valor valor.

5. Escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void imprimeArreglo(const int a[ ], int n, int c) La funci´on deber´a imprimir en la salida est´andar, los n elementos del arreglo a, con c elementos por rengl´on.

6. Escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void copiaArreglo(const int original[ ], int copia[ ], int n) La funci´on deber´a copiar los n elementos del arreglo original, en el arre- glo copia.

7. Escriba un programa que mida el tiempo en segundos para cada una de las funciones del Ejemplo 6.4. Su programa deber´a:

6.5. EJERCICIOS 137

Declarar dos arreglos de 32,767 elementos cada uno.

Utilizar una funci´on para inicializar uno de ellos con n´umeros alea- torios.

Usar una funci´on para copiar el arreglo inicializado en el segundo arreglo declarado.

Enviar uno de los arreglos a la funci´on burbuja y medir el tiempo que le toma ordenar los elementos.

Enviar el otro arreglo a la funci´on burbujaMejorado y medir el tiempo que le toma ordenar los elementos.

La intenci´on de este ejercicio es determinar si existe o no una mejor eficiencia con las modificaciones descritas en el texto, ya que el algo- ritmo de ordenamiento no cambia, s´olo se ha hecho, en principio, m´as eficiente.

8. Escriba un programa que pruebe las funciones del Ejemplo 6.5. Pruebe con datos le´ıdos desde el teclado, con datos aleatorios, y verifique que las funciones de b´usqueda trabajan de acuerdo a lo esperado.

Nota: No olvide que la b´usqueda binaria tiene como pre requisito que el conjunto de datos est´e ordenado.

9. Realice una prueba de escritorio para la funci´on busquedaBinaria del Ejemplo 6.5 para que busque las siguientes claves: -4, 22, 1 y 13. Utilice el siguiente conjunto de b´usqueda:

-20, -4, 0, -9, 7, 10, 15, 22

10. Realice una implementaci´on recursiva de la b´usqueda binaria en base a la descripci´on hecha en el texto respecto a la b´usqueda binaria, y una vez que haya realizado las pruebas de escritorio propuestas en el ejercicio anterior.

11. Siguiendo las ideas expuestas en la Secci´on 6.1.6, escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void copiaCadena(const char original[ ], char copia[ ]) La funci´on deber´a copiar los elementos de la cadena original, en la cadena copia. Puede apoyarse de las funciones desarrolladas hasta el momento.

138 CAP´ITULO 6. ARREGLOS

12. Siguiendo las ideas expuestas en la Secci´on 6.1.6, escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void invierteCadena(const char original[ ], char invertida[ ]) La funci´on deber´a copiar invertidos los elementos de la cadena origi- nal, en la cadena invertida. Puede apoyarse de las funciones desarrolla- das hasta el momento.

13. Un pal´ındromo es una expresi´on que representa o expresa lo mismo si se lee de izquierda a derecha que de derecha a izquierda, como por ejemplo:

Madam. 2002.

Anita lava la tina.

Dabale arroz a la zorra el abad.

entre cientos de pal´ındromos m´as.

Siguiendo las ideas expuestas en la secci´on 6.1.6, escriba un programa que defina y pruebe una funci´on con la siguiente firma:

int esPalindromo(const char c[ ])

La funci´on deber´a determinar si los elementos de la cadena c, consti- tuyen (regresa 1 ) o no (regresa 0 ) un pal´ındromo. Puede apoyarse de las funciones desarrolladas hasta el momento, y por ahora, asuma que los elementos de la cadena no contendr´an espacios y que todos los ca- racteres ser´an introducidos en min´usculas.

14. Escriba un programa que obtenga la CURP (Clave ´Unica de Registro de Poblaci´on). La CURP se obtiene de la siguiente forma:

Primera letra y primera vocal del primer apellido. Primera letra del segundo apellido.

Primera letra del nombre de la persona, en casos como Jos´e o Mar´ıa, de ser posible, se tomar´a la primer letra del segundo nom- bre.

´

Ultimas dos cifras del a˜no de nacimiento. Dos cifras del mes de nacimiento.

6.5. EJERCICIOS 139

Dos cifras del d´ıa de nacimiento .

Sexo de la persona: H (Hombre) o M (Mujer).

Dos Letras, correspondientes a la entidad de nacimiento. La enti- dad de nacimiento se obtiene como la primera letra de la entidad y la ´ultima consonante. En caso de extranjeros es NE (Nacido en el Extranjero).

Consonante del primer apellido (la primera si el primer apellido empieza con vocal, la segunda si empieza en consonante).

Consonante del segundo apellido (la primera si el primer apellido empieza con vocal, la segunda si empieza en consonante).

Consonante del nombre de la persona (la primera si el primer apellido empieza con vocal, la segunda si empieza en consonante). Dos d´ıgitos para evitar duplicidad (gen´erelos aleatoriamente). En casos en los que las personas no tienen segundo apellido, o que la letra que corresponde insertar sea la letra ˜N, se utilizar´a en su lugar la X.

Verifique estas reglas para su propia CURP.

15. Considere el Ejemplo 6.7. ¿Qu´e pasa si hay m´as inicializadores que elementos f´ısicos? Pruebe ´esta situaci´on en los dos esquemas de inicia- lizaci´on planteados en el texto.

16. Escriba un programa que pruebe la funci´on suma del Ejemplo 6.8.

17. Modifique la funci´on leeMatriz del Ejemplo 6.7 para que almacene va- lores de tipo float en la matriz matriz. Una vez que haya probado su funci´on, agr´eguela a la biblioteca de funciones del Ejemplo 6.8.

18. Modifique la funci´on imprimeMatriz del Ejemplo 6.7 para que imprima los valores de tipo float de la matriz matriz. Una vez que haya probado su funci´on, agr´eguela a la biblioteca de funciones del Ejemplo 6.8.

19. Escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void resta(float a[ ][N], float b[ ][N], float c[ ][N], int m, int n)

140 CAP´ITULO 6. ARREGLOS

La funci´on deber´a calcular en la matriz c la resta de la matriz a menos la matriz b, de manera an´aloga a como lo hace la funci´on suma del Ejemplo 6.8.

20. Agregue la funci´on resta del ejercicio anterior, a la biblioteca de fun- ciones del Ejemplo 6.8.

21. Sea A una matriz definida de la siguiente manera:

A =  a0,0 ... a0,n−1 .. . ... ... am−1,0 ... am−1,n−1  m×n (6.6)

La matriz transpuesta de A, denotada por AT, est´a definida como: (AT)ij = Aji, 0 ≤ i ≤ n − 1, 0 ≤ j ≤ m − 1 (6.7) es decir:  a0,0 ... am−1,0 .. . ... ... a0,n−1 ... am−1,n−1  n×m (6.8)

Escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void imprimeTranspuesta(float a[ ][N], int m, int n)

La funci´on deber´a imprimir en la salida est´andar, la matriz transpues- ta de a, siguiendo los lineamientos de las Expresiones 6.7 y 6.8.

22. Escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void transpuesta(float a[ ][N], float aT[ ][N],int m, int n)

La funci´on deber´a generar en la matriz aT, la matriz transpuesta de a, siguiendo los lineamientos de las Expresiones 6.7 y 6.8.

23. Agregue la funci´on transpuesta del ejercicio anterior, a la biblioteca de funciones del Ejemplo 6.8.

6.5. EJERCICIOS 141

24. Sea A una matriz definida de la siguiente manera:

A =  a0,0 ... a0,n−1 .. . ... ... am−1,0 ... an−1,n−1  n×n (6.9)

Se dice que la matriz A es sim´etrica respecto a la diagonal principal si:

aij = aji, ∀i 6= j (6.10)

Escriba un programa que defina y pruebe una funci´on con la siguiente firma:

int esSimetrica(float a[ ][N], int n)

La funci´on deber´a determinar si la matriz a, es o no sim´etrica en base a lo expuesto en la Expresi´on 6.10.

25. Agregue la funci´on esSimetrica del ejercicio anterior, a la biblioteca de funciones del Ejemplo 6.8.

26. Sean A y B dos matrices definidas de la siguiente manera:

A =  a0,0 ... a0,n−1 .. . ... ... am−1,0 ... am−1,n−1  m×n B = b0,0 ... b0,l−1 .. . ... ... bn−1,0 ... bn−1,l−1 ! n×l (6.11)

El producto de Am×n× Bn×l denotado por Cm×l, se define como:

Cij = n−1 X r=0 airbrj (6.12) es decir: a0,0b0,0+...+a0,n−1bn−1,0 ... a0,0b0,l−1+···+a0,n−1bn−1,l−1 .. . ... ... am−1,0b0,0+...+am−1,n−1bn−1,0 ... am−1,0b0,l−1+...+am−1,n−1bn−1,l−1 ! n×l (6.13)

Escriba un programa que defina y pruebe una funci´on con la siguiente firma:

void producto(float a[ ][N], float b[ ][N], float c[ ][N], int m, int n, int l)

142 CAP´ITULO 6. ARREGLOS

de tal forma que la funci´on calcule el producto de las matrices a y b almacenando el resultado en c. Las matrices tienen la forma descrita en la Expresi´on 6.11; y el producto se calcula siguiendo los lineamientos expuestos en las Expresiones 6.12 y 6.13.

27. Agregue la funci´on producto del ejercicio anterior, a la biblioteca de funciones del Ejemplo 6.8.

28. La Figura 6.9 muestra en gris algunos de los elementos del arreglo de tres dimensiones. Denote todos los elementos visibles (para ambas representaciones (a) y (b)) de dicho arreglo siguiendo la nomenclatura del lenguaje C.

29. De manera an´aloga a lo descrito con anterioridad para los arreglos y las matrices, dibuje y describa la representaci´on f´ısica en la memoria de La Figura 6.9.

Sugerencia: la representaci´on en memoria se realiza tambi´en de mane- ra lineal: los renglones de la primera matriz, despu´es los de la segunda matriz, etc´etera, la representaci´on de malla puede ayudarle a visualizar mejor la representaci´on.

30. Bas´andose en el Ejemplo 6.9, escriba un programa que generalice la idea planteada para definir, inicializar, e imprimir en la salida est´andar un hipercubo.

31. Bas´andose en el Ejemplo 6.9, escriba un programa que generalice la idea planteada para definir, inicializar, e imprimir en la salida est´andar un arreglo de cinco dimensiones, cada una con un tama˜no distinto, por ejemplo: dos para la primera dimensi´on, tres para la segunda, cuatro para la tercera y as´ı sucesivamente.

Cap´ıtulo 7

Apuntadores

Los apuntadores son uno de los tab´ues del lenguaje de programaci´on C1.

Este cap´ıtulo introduce a los conceptos y manipulaci´on de apuntadores, la principal intenci´on es que, al final de su lectura, los apuntadores dejen de ser un tab´u y pasen a ser parte del repertorio de herramientas del programador, ya que los apuntadores son, en m´as de un sentido, la piedra angular del lenguaje de programaci´on C.

7.1.

Definici´on, estructura y representaci´on

La sencillez de los apuntadores se basa en su definici´on, la cual es bastante f´acil de recordar y es, en esencia, casi todo lo que se tiene que saber respecto a los apuntadores, de ah´ı la importancia de su comprensi´on.

Un apuntador es una variable cuyo contenido es la direcci´on en memoria de otra variable.

La Figura 7.1 muestra la representaci´on de un apuntador, y su explicaci´on se ver´a reforzada cuando se analice el Ejemplo 7.1.

Mientras tanto, tome en cuenta que un apuntador s´olo puede hacer refe- rencia o apuntar, a objetos de su mismo tipo de datos, esto es: un apuntador a int, s´olo puede hacer referencia a variables de tipo int, un apuntador a char, s´olo puede hacer referencia a variables de tipo char, etc.

En este sentido, la declaraci´on de una variable de tipo apuntador tiene la siguiente estructura general en el lenguaje de programaci´on C:

1Otro tab´u es la recursividad, aunque la recursividad no est´a asociada a ning´un len- guaje, sino con la programaci´on.