You are on page 1of 119

INTRODUCCION

Como primer ejemplo de un programa en C, vamos a considerar el programa "Jalisco". Este programa le pregunta al usuario un nmero, para despus ganarle diciendo el sucesor. La pantalla muestra la salida del programa: Por favor ingresa un nmero: 4944 Lo siento, te gano con el 4945 Este programa podra escribirse en muchos lenguajes distintos, cmo C, C++, Pascal, Turing, Smalltalk, etc. Nosotros usaremos como lenguaje de programacin C. Una vez que uno aprende un lenguaje, se hace muy fcil aprender lenguajes que tengan un paradigma similar. El programa Jalisco, escrito en C, es el siguiente 1. /* Jalisco nunca pierde */ 2. main () 3. { 4. /* Variables */ 5. int n; 6. printf ("Por favor ingresa un nmero:"); 7. scanf (%d,&n); 8. printf ("Lo siento, te gano con el "); 9. printf (%d \n,n+1); 10. } Por ahora, nos concentraremos en la parte principal del programa, es decir, en las lneas 3 a la 10. El significado y utilidad de las otras lneas vamos a analizarlos ms adelante.

Lnea 4

Ejecucin Un comentario sirve para ayudarle al programador a recordar qu hace cada parte de un programa. Todos los comentarios empiezan con /* y terminan con */

Esta instruccin declara una variable para guardar nmero enteros (int) cuyo identificador es n. Puedes imaginarte una variable como un cajn en la memoria donde se puede guardar un valor (que puede ir cambiando durante el transcurso del programa, de ah el nombre "variable"). Esto se puede esquematizar de la siguiente forma: n _____

El programa escribe en la pantalla un mensaje para el usuario: Por favor ingresa un nmero: Para mostrar un mensaje se utiliza el mtodo printf, el cual escribe su argumento (lo que va entre parntesis) en pantalla

Esta instruccin le asigna a n un nmero entero que se lee desde teclado. Para avisarle al usuario que se est esperando el ingreso de un nmero, C muestra un pequeo cuadrado parpadeante llamado cursor: Por favor ingresa un nmero: C espera a que el usuario escriba el nmero y presione la tecla Enter. Cuando esto sucede, C le asigna el nmero que ley a la variable de nombre n. Supongamos que el usuario ingresa el nmero 512 y presiona Enter: Por favor ingresa un nmero: 512 En la variable n se almacena este nmero n _512_

Esta instruccin muestra un nuevo mensaje en la pantalla: Por favor ingresa un nmero: 512 Lo siento, te gano con el

Finalmente, C calcula cul es el nmero que sigue a n y lo muestra en la pantalla Por favor ingresa un nmero: 512 Lo siento, te gano con el 513

Las variables en C En C uno puede guardar informacin en elementos llamados variables. Cada variable tiene un tipo, que indica qu es lo que se guardar en ella. Existen varios tipos numricos, entre los cuales se encuentran los nmeros enteros (int) y los nmeros reales (double). Las variables en C son declaradas de la siguiente forma: tipo identificador; Por ejemplo, 11. 12. int alumnos; double temperatura;

La declaracin tiene que preceder el uso de la variable en un programa. Por ejemplo, es incorrecto:

1. 2.

alumnos=106; int alumnos; Una vez que la variable ha sido declarada, su tipo no puede cambiarse. Tambin es incorrecto:

2. 3. 4. 5. 6. 7. 8.

int manzanas; manzanas=7; printf ("Hay %d manzanas\n",manzanas); printf ("Me como media"); float manzanas; manzanas=6.5; printf ("Ahora hay %f manzanas\n",manzanas); Pon tus conocimientos a prueba

Escribe un programa en C que calcule el rea y permetro de un crculo. La siguiente pantalla muestra el dilogo del programa: 9. Ingresa el radio del crculo (cm): 3.1 10. El permetro del crculo es 19.477 11. El rea del crculo es 30.191 12. Solucin

13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24.

El programa es el siguiente: /* rea y permertro de crculo */ main () { float radio, pi=3.141592654; printf ("Ingresa el radio:"); scanf (%f,&radio); printf ("El rea es "); printf (%f\n,pi*radio*radio); printf ("El permetro es"); printf (%f\n,2*pi*radio); }

Analicemos qu sucede durante la ejecucin del programa: Lnea 23 Ejecucin Esta instruccin declara una variable para guardar nmero reales (double) cuyo identificador es radio. radio _____ 24 El programa escribe en la pantalla un mensaje para el usuario: Ingresa el radio: 25 Se le asigna a radio un nmero real que se lee desde teclado. Ingresa el radio: 3.0 radio _3.0_ 26 Esta instruccin muestra un nuevo mensaje en la pantalla: Ingresa el radio:3.0 El rea es: 27 C calcula cul es el rea del crculo, es decir, el producto de la constante pi y el cuadrado de radio. Ingresa el radio:3.0 El rea es: 28.274 28 C muestra un nuevo mensaje en la pantalla

Ingresa el radio:3.0 El rea es: 28.274 El permetro es: 29 Para finalmente calcular el permetro y mostrarlo Ingresa el radio:3.0 El rea es: 28.274 El permetro es: 18.8495

Expresiones Las variables de tipos numricos pueden ser combinados mediante operadores aritmticos para formas expresiones. Los operadores aritmticos se ven en la siguiente tabla: Smbolo + * / % Operador suma resta multiplicacin divisin mdulo

Las precedencia son iguales a las del lgebra: 1. expresiones entre parntesis 2. multiplicacin y divisin 3. suma y resta En caso de dos o ms operadores de igual precedencia, se evala de izquierda a derecha. Ejemplos: Expresin Escrita correctament e (a-b)/(2*c) (a-b)/2/c a+b*c a+(b*c) En caso de dudas, siempre se puede usar parntesis, total son gratis. Tipo de una expresin Escrita incorrectamente a-b/2*c (a-b)/2*c (a+b)*c

Para conocer el tipo de una expresin, se puede utilizar la siguiente regla: Expresin Simple Tipo double si x o y es double 9/2.0=4.5 8/2.0=4.0 9/2=4 int si ambos son int Compuesta segn la precedencia de los operadores se aplica la regla anterior 8/2=4 Si d es una variable double y t una variable int, entonces (d/1000) / (t/3600) (double/int) / (int/int) double / int double El operador mdulo (%) En C existe un operador muy til llamado mdulo, muy relacionado con la divisin y que se denota por %. El mdulo entre dos nmeros enteros a y b es el resto de la divisin entera entre ambos. Por ejemplo, 7/2 3 7%2 1 Ejemplo

Qu hace el siguiente programa? 1. int s,minutos,segundos; 2. printf ("Ingresa la cantidad de segundos transcurridos"); 3. scanf(%d,&s); 4. printf ("Eso equivale a "); 5. minutos=s/60; 6. printf (%d,minutos); 7. printf (" minutos y "); 8. segundos=s%60; 9. printf (%d\n,segundos); Asignacin El almacenamiento de valores en las variables se lleva a cabo mediante la asignacin (=). Una variable puede ser asignada reiteradas veces. Por ejemplo, 10. 11. 12. 13. 14. int dolar; dolar=480; printf ("El valor del dlar es %d\n",dolar); dolar=500; printf ("El nuevo valor del dlar es %d\n".dlar);

No solo se puede asignar valores numricos fijos, tambin expresiones 15. double g=9.87,t,v; 16. printf ("Cuanto tiempo ha transcurrido desde que se solt el objeto?\n"); 17. scanf(%f,&t); 18. v=g*t; 19. printf ("La velocidad es de %f\n",v); Una de las cosas que no siempre se puede hacer es asignar una expresin de un tipo a una variable de otro tipo. Por ejemplo, el siguiente cdigo es incorrecto 20. int superficie=3.1416*radio*radio;

Ya que se est tratando de guardar un nmero real en una variable que ha sido declarada como real. Es como tratar de guardar pelotas de tenis en un envase de pelotas de ping-pong.

Una vez que una variable ha sido asignada, su valor no se modifica hasta que se le vuelva a hacer una asignacin Inicializacin Una variable no puede ser usada en una expresin hasta que se le ha dado un valor. Se llama inicializacin a la primera asignacin de una variable. 21. 22. double pi; pi=3.14159;

Como ya hemos visto, C permite inicializar una variable en la misma instruccin en la que se le declara: 23. double pi=3.14159; Asignaciones con la variable asignada en la expresin C permite realizar asignaciones donde la variable asignada aparece en la expresin de la derecha: 24. 25. int conejos=5; conejos=conejos+1;

C evala primero la expresin de la derecha y luego asigna ese valor a la variable. En este caso C calcula cunto vale conejos+1. Recin cuando sabe que el valor es 6, lo asigna a conejos. El incremento de una variable, es decir, una asignacin del tipo n=n+1;

se ven tan a menudo en C que existe una notacin abreviada para ella n++; Lo mismo para el decremento: n--; es lo mismo que n=n-1; "Swap" del valor de dos variables Imagina que tienes dos variables enteras, x e y, que son inicializadas con el valor 5 y con un nmero ledo desde el teclado, como se ve en el cdigo X 26. 27. 28. int x=5; int y; scanf(%d,&y);

Cmo haras para intercambiar el valor de ambas variables? Una solucin que puede parecer correcta es 29. 30. x=y; y=x;

Sin embargo lo que hace realmente C es dejar en x e y el valor original de y, ya que primero se le asigna el valor de y a x, con esto el valor de x se pierde. Despus se le asigna este valor a y, que no se ve modificado, pues x tiene el valor original de y. Una solucin consiste en usar una variable auxiliar. Hay variables que no se ocupan para guardar valores en forma temporal, generalmente para facilitar la realizacin de un clculo o tarea. Estas variables reciben el nombre de variables auxiliares.

En este caso vamos a usar una variable auxiliar para guardar el valor original de x 31. 32. 33. int auxiliar=x; x=y; y=auxiliar; Funciones predefinidas Al igual que las calculadoras cientficas, C trae incorporado una gran gama de funciones en la librera math.h, entre las que se encuentran las siguientes: sqrt(x) abs(x) raz cuadrada de x valor absoluto de x

pow(x,y) x "elevado a" y exp(x) log(x) sin(x) cos(x) tan(x) asin(x) Acos(x) Atan(x) exponencial de x logaritmo natural de x seno de x (radianes) coseno de x (radianes) tangente de x (radianes) arcoseno de x (radianes) arcocoseno de x (radianes) arcotangente de x (radianes)

round(x) x redondeado. Por ejemplo, round (3.6) es 4, round(3.4) es 3 Floor(x) ceil(x) piso de x. Por ejemplo, floor (3.6) es 3, al igual que floor (3.4) techo de x. ceil (3.6) y ceil (3.4) es 4

trunc(x) devuelve la parte entera de x (el tipo de la funcin es int) Por ejemplo, para calcular el rea de un crculo cuyo radio es radio la expresin en C es double area=3.1416*pow(radio,2); Para guardar en una variable entera los grados del arcotangente de (0.3) se puede usar

int grados=trunc(180.0*atan(0.3)/3.1416); Problemas resueltos Problema 1: Maquina reversadora de cifras Haz un programa que reciba un numero de 5 digitos (suponga que lo ingresan bien) y muestre el nmero en forma reversa: Maquina reversadora de numeros Ingrese un numero: ? 12345 Su numero en forma normal es 12345 Su numero al reves es 54321 Solucin: 34. int numero, digito1, digito2, digito3, digito4, digito5, alreves; 35. printf("Maquina reversadora de numeros"); 36. printf("Ingrese un numero: ? "); 37. scanf(%d,&numero); 38. /* desmenuzamos el numero */ 39. digito1= numero / 10000; 40. digito2= (numero % 10000) / 1000; 41. digito3= (numero % 1000) / 100; 42. digito4= (numero % 100) / 10; 43. digito5= (numero % 10); 44. /* lo armamos de nuevo */ 45. alReves= digito5 * 10000 + digito4 * 1000 + digito3 * 100 + digito2 * 10 + digito1; 46. // mostramos el resultado 47. printf("Su numero en forma normal %d \n ", numero); 48. println("Su numero al reves es %d \n", alReves); Problema 2: Calculo de distancias Haga un programa que pregunte por las coordenadas de dos puntos en el plano y calcule la distancia (euclidiana) entre ellos. Calculo de distancia entre dos puntos Coordenadas x y del primer punto ? 10.2 4.5

Coordenadas x y del segundo punto ? 7.5 -3.5 Solucin: 49. double x1,y1,x2,y2,distancia; 50. printf ("Calculo de distancia entre dos puntos \n"); 51. printf("Coordenadas x y del primer punto ? "); 52. scanf(%f,&x1); 53. scanf(%f,&y1); 54. print("Coordenadas x y del segundo punto ? "); 55. scanf(%f,&x2); 56. scanf(%f,&y2); 57. distancia = sqrt( pow((x1-x2),2) + pow ((y1y2),2) ); 58. printf ("La distancia euclidiana entre %f,%f y %f, %f es %f \n ,x1,y1,x2,y2,distancia ); Pon tus conocimientos a prueba 1. El almacen "Don Manolo" necesita un programa que ayude a calcular la forma de dar vuelta con el mnimo de billetes y monedas posible. La pantalla X muestra el dilogo que debe mantener el programa 59. Total a pagar: 7823 60. El cliente paga: 10000 61. Vuelto = 2177 62. Desglosado en: 63. 0 billete(s) de 10000 64. 0 billete(s) de 5000 65. 2 billete(s) de 1000 66. 0 billete(s) de 500 67. 1 moneda(s) de 100 68. 1 moneda de 50 69. 2 moneda(s) de 10 70. 1 moneda de 5 71. 2 moneda(s) de 1 72. 2. Qu escribe el siguiente programa en la pantalla? 73. int a=5,b=3,c=1; 74. printf ("Los valores de las variables son %d %d %d",a,b,c); 75. a=b; 76. b++; 77. printf ("Los valores de las variables son %d %d %d",a,b,c); 78. c--;

79. a=c; 80. printf ("Los valores de las variables son %d %d %d",a,b,c); 81. c--; 82. a=2*c-b; 83. printf ("Los valores de las variables son %d %d %d",a,b,c); 3. Cmo haras un swap de dos variables sin usar una variable auxiliar? Este problema de ingenio es realmente difcil. Intenta resolverlo. Errores (demasiado) comunes Entre los errores ms comunes detectados en el primer control de Computacin I destacan los siguiente:

Ocupar una variable sin declararla Ocupar una variable sin inicializarla Ocupar nombres cortos de variables: si bien esto no es un error, en un programa largo puede inducir a errores. Es una mala tcnica tratar de "ahorrar tiempo" acortando el identificador de una variable. Para almacenar el radio de una figura es mucho mejor usar una variable radio en vez de una variable p. Asignar un double a un int sin usar la funcin de conversin trunc ( ). Esto error es detectado en tiempo de compilacin. Inventar operadores o notaciones: C (correcto) area=3.1416*radio*radio; radio=diametro/3.1416; radio=dimetro/pi; (si pi ha sido declarado)

Notacin "inventada" por alumno

Recuerda siempre que los lenguajes de programacin son sumamente extrictos con respecto a la sintxis (cmo se escriben las cosas)!

IF Y ELSE
Hasta ahora los programas que hemos visto consisten en una serie de instrucciones que se van ejecutando una tras otra en forma secuencial, como se ve en la figura 1:

Figura 1 Figura 2 Sin embargo hay ocasiones en las que uno puede querer que una instruccin se ejecute slo si se cumple una condicin determinada, como se ve en la figura 2. En C esto se lleva a cabo gracias a la sentencia "if", que permite ejecutar una instruccin (o bloque de instrucciones) dependiendo del valor de verdad de una expresin lgica.

En el siguiente programa se muesta el uso de if para calcular el mximo de dos nmeros: 1. 2. 3. 4. 5. 6. 7. 1-3 4 5,6 7 int a,b,maximo; printf ("Ingresa dos nmeros"); scanf(%d %d,&a,&b); mximo=a; if (b>a) mximo=b; printf("El mximo es %d",mximo); Ejecucin Se pide al usuario que escriba dos nmeros desde el teclado Se inicializa la variable mximo con el valor de a Si b es mayor que a entonces el valor de b se le asigna a mximo Se escribe el mximo en la pantalla

Lnea(s)

8. Estructura de if simple 9. La instruccin if ms simple consiste en la palabra if seguida de una condicin entre parntesis y la instruccin que se debe ejecutar si la condicin es verdadera: 10. if (condicin) 11. instruccin; Si la condicin no se cumple, entonces la instruccin no se ejecuta y se pasa a la siguiente instruccin. Condiciones Una condicin es una expresin que tiene un valor verdadero o falso o una combinacin de ellas. En este captulo analizaremos las expresiones en las que comparamos expresiones aritmticas entre s.

Operadores de relacin, su significado y un ejemplo: Operador == < > <= >= != Significado igual a menor que mayor que Ejemplo (x==10) (x<y) (x+5>y)

menor o igual (2*x<=-4) a mayor o igual (valorAntiguo>=valorNuevo) a distinto de (x!=18/y)

Las condiciones compuestas se forman a partir de condiciones simples unidas mediante los operadores lgicos O, Y y negacin: Operador || && ! Significado O lgico Y lgico negacin Ejemplo (total<=0 or total>=100) (puntaje==600 && lugar<500) (!(lugar<minimo)) If-else Muchas veces, lo que nos interesa es ejecutar una instruccin si se cumple una determinada condicin y ejecutar otra si no se cumple. Esquemticamente:

Por ejemplo, 12. 13. 14. 15. 16. 17. 18. Lnea(s) int a; printf ("Ingresa el ao:"); scanf(%d,&a); if (a%4==0 && a%100!=0) printf ("El ao es bisiesto\n"); else printf ("El ao es normal\n"); Ejecucin

10...12 Se pide un ao y se guarda en la variable ao 13 14 15-16 Se evala la condicin "es el ao divisible por 4 y no por 100?" Si la condicin es verdadera se escribe "El ao es bisiesto en la pantalla" Si la condicin es falsa, se muestra el mensaje "El ao es normal" 19. Bloques de instrucciones

20. Si uno quiere que se ejecute ms de una instruccin si la condicin del if se cumple uno puede agrupar las instrucciones en un bloque: 21. float a,b,c,d; 22. printf ("Ingresa los coeficientes a, b y c"); 23. scanf (%f %f %f,&a,&b,&c); 24. d=b*b-4*a*c; 25. if (d>0) { 26. float x1, x2; 27. printf ("Las soluciones son reales y valen"); 28. x1=(-b+sqrt(d))/(2*a); 29. x2=(-b-sqrt(d))/(2*a); 30. print ("%f y %f\n",x1,x2); 31. } if (discriminante==0) { 32. float x; 33. printf ("Existe una sola solucin real "); 34. x=(-b+ sqrt(d))/(2*a); 35. printf (%f\n,x); 36. } else { 37. float real, imaginario; 38. printf ("Las soluciones son complejas y valen"); 39. imaginario=sqrt(-d))/(2*a); 40. real=-b/(2*a); 41. printf (%f + %fi y,real,imaginario); 42. printf (%f - %fi \n,real,imaginario); 43. } Sigamos la ejecucin: Lnea(s) 17-19 20 21-26 27-31 Ejecucin Se leen desde el teclado los coeficientes de la ecuacion ax2+bx+c=0 Se calcula el discriminante Si el determinante es mayor que 0, entonces se muestran las dos soluciones reales Si el determinante es igual a 0, entonces se muestra la nica solucin real Nota: En la prctica no es buena idea comparar un nmero de tipo double con otro nmero de tipo double por problemas de representacin interna. 33-38 Si ninguna de las dos condiciones anteriores se ha cumplido, entonces se escribe el valor de las dos

soluciones complejas Un bloque de instrucciones es un conjunto de instrucciones que actan como si fueran una sola. Por lo general, C permite colocar un bloque de instrucciones en el contexto donde se puede colocar una instruccin. Un bloque de instrucciones comienza con un parntesis { y termina con un parntesis }. Problemas resueltos Problema 1: Suma de fracciones La suma de dos nmeros racionales se define como

Escribe un programa que lea dos nmero racionales y escriba el resultado. Si el resultado es un nmero entero, escrbelo de esa forma tamben. Fjate que los denominadores sean distintos de 0. Si no es as, en vez de escribir el resultado escribe una advertencia que diga que el resultado es errneo Solucin: 44. int num1,den1,num2,den2,num,den; 45. /* Preguntar los numeradores y denominadores */ 46. printf ("Ingresa el numerador de la primera fraccion: "); 47. scanf(%d,&num1); 48. printf ("Ingresa el denominador de la primera fraccion: "); 49. scanf(%d,&den1); 50. printf ("Ingresa el numerador de la segunda fraccion: "); 51. scanf(%d,&num2); 52. printf ("Ingresa el denominador de la segunda fraccion: "); 53. scanf(%d,&den2); 54. num=num1*den2+num2*den1 55. den=den1*den2 56. if (den==0) 57. printf ("Uno de los dos denominadores ingresados es 0\n");

58. 59. 60. 61.

else printf("El resultado es %d/%d",num,den); if (num%den==0) printf("Lo que es equivalente a %d",num/den);

Pon tus conocimientos a prueba

Escribe un programa que lea un nmero desde el teclado y escriba si es par o no Escribe un programa que lea tres nmeros del teclado y los escriba ordenados de menor a mayor

Errores (demasiado) comunes Un error comn es agregar una clausula else que no hace nada. Aunque esto no es erroneo el cdigo se vuelve ms complicado de entender. Por ejemplo, la clausula else del siguiente programa est dems. Esta trata de representar "si la velocidad supera los 50 kilometros por hora, entonces hay que subir el cambio, si no, el cambio se mantiene"

1. 2. 3. 4.

if (velocidad>50) cambio=cambio+1; else cambio=cambio; Lo correcto es

5. if (velocidad>50) 6. cambio=cambio+1;

CICLOS (while y for)


Escribe un programa que lea dos nmeros enteros y escriba todos los nmeros mayores que el primero y menores que el segundo Ingresa el primer nmero: 38 Ingresa el segundo: 45 Los nmeros son: 39 40 41 42 43 44 Muchas gracias por usar este programa Un intento (incorrecto) de solucin es: 1. int a,b; 2. printf ("Ingresa el primer nmero :"); 3. scanf (%d,&a); 4. printf ("Ingresa el segundo:"); 5. scanf (%d,&b); 6. print ("Los numeros son"); 7. if (a+1<b) printf (%d\n,a+1); 8. if (a+2<b) printf (%d\n,a+2); 9. if (a+3<b) printf (%d\n,a+3); 10. if (a+4<b) printf (%d\n,a+4); 11. if (a+5<b) printf (%d\n,a+5); 12. if (a+6<b) printf (%d\n,a+6); 13. if (a+7<b) printf (%d\n,a+5); 14. if (a+8<b) printf (%d\n,a+6); 15. ... etctera Flujo de un programa Hasta ahora todos los programas que hemos visto se caracterizan por el hecho de que las instrucciones se van ejecutando en orden y una sola vez. Esto impide realizar un programa como el queremos hacer. Sin embargo, C, al igual que muchos otros lenguajes, permite repetir una cierta cantidad de instrucciones mientras una condicin se cumpla. Esta instruccin de control de flujo se llama while, se esquematiza en la figura 1 y su sintaxis es:

while (condicion) { instrucciones; } while evala la condicin entre parntesis y si esta es verdadera, ejecuta todas las instrucciones entre los parntesis { }. Cuando ha ejecutado la ltima instruccin, vuelve a evaluar la condicin. El proceso se repite hasta que al terminar de ejecutar las instrucciones se evala la condicin y esta es falsa.

Figura 1 En caso que slo se repita una sola instruccin, se pueden eliminar los parntesis while (condicion) instruccin; Un ejemplo Un primer ejemplo de while se ve en el siguiente programa: 16. 17. 18. int a,b,i; printf ("Ingresa el primer nmero: "); scanf(%d,&a);

19. printf ("Ingresa el segundo:"); 20. scanf(%d,&b); 21. printf ("Los numeros son"); 22. i=a+1; 23. while (i<b) { 24. printf (%d ",i); 25. i=i+1; 26. } 27. printf ("\nMuchas gracias por usar este programa\n"); Lnea(s) 16-20 Ejecucin Se escriben los mensajes en la pantalla y se leen las respuestas del usuario desde el teclado Ingresa el primer nmero: 38 Ingresa el segundo: 45 Los nmeros son : 21 22 La variable auxiliar i se va a usar para escribir los valores a<i<b. Se inicializa con a+1, en este caso, 39. Si i es menor que b, entonces las instrucciones en las lneas 24 a 25 se ejecutan una vez. Si no es as se sigue la ejecucin con la lnea 27 Se escribe el valor de i Ingresa el primer nmero: 38 Ingresa el segundo: 45 Los nmeros son : 39 25 El valor de i se incrementa en 1 i __40__ 22 Cuando se llega al final de las instrucciones, el programa se devuelve a la instruccin while y la condicin se reevala. En este caso se compara i (40) con b (45). Como i es menor que b la condicin es verdadera y nuevamente se ejecutan las lneas 22 a 23 Se escribe el valor de i y luego se incrementa

24

24-25

Ingresa el primer nmero: 38 Ingresa el segundo: 45 Los nmeros son : 39 40 i __41__ 22 Como nuevamente se llego al final de las instrucciones dentro del while, se reevalua la condicin del while, que es verdadera y se vuelve a iterar. ... El programa ha seguido iterando, y se ha entrado a las instrucciones con un valor de i de 44. Se escribe el valor de i y se incrementa Ingresa el primer nmero: 38 Ingresa el segundo: 45 Los nmeros son : 39 40 41 42 43 44 i __45__ 22 Se vuelve a evaluar la condicin del while, en este caso es falsa. C salta inmediatamente a la lnea que sigue a las instrucciones del while, es decir, la lnea 26 Se escribe el mensaje de agradecimiento y finaliza el programa Ingresa el primer nmero: 38 Ingresa el segundo: 45 Los nmeros son : 39 40 41 42 43 44 Muchas gracias por usar estre programa 28. A veces existen muchas formas de llevar a cabo una tarea. En el caso del programa que acabamos de analizar se puede usar un contador en el ciclo while. 29. Un contador es una variable auxiliar que se usa junto a un while para repetir un trozo de cdigo una cierta cantidad de veces. En

... 24-25

26

nuestro ejemplo, sabemos que la cantidad de nmeros entre a y b son b-a-1. Por ejemplo, si a vale 5 y b 8, hay que escribir 8-5-1=2 nmeros, que son 6 y 7. 30. Una nueva versin del programa es: 31. printf ("Ingresa el primer nmero: "); 32. ... 33. printf ("Los numeros son"); 34. i=b-a-1; 35. while (i>0) { 36. print (%d ,b-i); 37. i=i-1; 38. } Otra versin, igualmente correcta es: 39. 40. 41. 42. 43. 44. 45. 46. printf ("Ingresa el primer nmero: "); ... printf ("Los numeros son"); i=b-a-1; while (i>0) { i=i-1; printf (%d,b-(i+1)); }

Una ejecucin del grupo de instrucciones se denomina iteracin. Si se ejecuta 10 veces el grupo de instrucciones, se dice que el ciclo tuvo 10 iteraciones. No existe un lmite al nmero de iteraciones que se pueden llevar a cabo en un ciclo. mbito de una variable

El mbito de una variable es la parte del programa donde la variable es conocida y puede ser usada. Como ya vimos, una variable puede ser usada slo despus que se ha definido. En C, el mbito de la variable termina cuando se encuentra el parntesis } que cierra al parntesis { que preceda la declaracin de la variable. Cualquier referencia a una variable tiene que ser hecha dentro del mbito de la variable, si no, C no compila el programa. La siguiente tabla muestra el mbito de las variables del cdigo de ejemplo:

a b c int a;

Cdigo

scanf(%d,&a); while (a<5) { int b=a*2,c; Printf (%d,b); c=a-b-2; Printf (%d,c); }

Por ejemplo, sera errneo usar la variable b c fuera del while, ya que al ser declaradas dentro de l, su mbito termina al cerrarse el bloque de instrucciones. Las variables que son declaradas dentro de un while pierden su valor al fin de cada iteracin, y se declaran nuevamente en la siguiente iteracin (como si fueran "variables nuevas").

Indentacin del while Indentar es ordenar un programa agregando espacios al lado izquierdo de las instrucciones. Dos formas de indentar un while son las siguientes: Parentesis de comienzo de bloque en la misma lnea de la clusula while
while (condicin) { instruccin; instruccin; instruccin; }

Parntesis de comienzo de bloque en una nueva lnea

while (condicin) { instruccin; instruccin; instruccin; } En ambos casos se deja un espacio antes del comienzo de cada instruccin dentro del bloque para clarificar el programa. Nunca olvides indentar. Si bien no es obligacin hacerlo te puede simplificar bastante entender lo que hace el programa. Whiles anidados Es perfectamente posible colocar un while dentro de otro while (while anidado). Como ejemplo, considera el siguiente programa, que escribe la tabla de multiplicar del 1 al 10:

1. 2. 3. 4. 5. 6. 7. 8.

int i=0; while (i<10) { int j=0; while (j<10) { printf ("%d x %d es %d ",i,j,(i*j)); j++; } i++;

9. } Break La instruccin break interrumpe la ejecucin del while que la contiene. Es til en algunos contextos donde una condicin hace que no sea deseable seguir con la iteracin. El siguiente pseudo-cdigo muestra un contexto donde break es til:

while (jugador1 ni jugador2 ha ganado) { jugador1 mueve pieza if jugador1_gan { escribe_mensaje (gan jugador 1) break; } jugador2 mueve pieza if jugador2_gan escribe_mensaje (gan jugador 2) } Errores (demasiado) comunes Un error muy comn es creer que un while puede terminar (salir de) en la mitad del bloque de instrucciones, bastando que una instruccin haga que la condicin del while sea falsa, olvidando que lo que realmente ocurre es que C evala la condicin del while antes de entrar por primera vez al bloque y antes de reentrar en cada iteracin.

Considera el siguiente programa: 1. 2. 3. 4. 5. 6. int i=3; while (i>1) { i=i-1; printf (%d,i); } printf ("fin"); La traza (orden en que se ejecutan las instrucciones) de este programa es (1) (2) (3) (4) (2) (3) (4) (2) (6) y no (1) (2) (3) (4) (2) (3) (6)

como podra pensar alguien que crea que el while termina abruptamente en la lnea 44 cuando el valor de i se hace 1 (y por lo tanto la condicin del while falsa) Otro error es olvidar cual es el mbito de una variable declarada dentro de un while. El siguiente programa, que calcula n!, tiene dos errores: 1. 2. 3. 4. 5. 6. 7. 8. 9.

int i,n; printf ("ingresa n:"); scanf(%d,&n); i=1; while (i<=n) { int factorial=factorial*i; i=i+1; } printf ("Factorial %d\n",factorial); En la lnea 6 se est tratando de asignar a la variable factorial su producto con i (lo que es indefinido, ya que factorial, al estar recin declarada no tiene valor) En la ltima lnea, se hace referencia a la variable factorial, cuyo mbito va de la lnea 6 a 7.

La solucin correcta, que solo modifica ligeramente el programa, es: 1. 2. 3. 4. 5. 6. 7. 8. 9. int i,n,factorial=1; printf ("ingresa n:"); scanf(%d,&n); i=1; while (i<=n) { factorial=factorial*i; i=i+1; } printf ("Factorial %d\n",factorial); Problemas resueltos Problema 1: Factores de un nmero Escribe un programa que pida ingresar un nmero mayor que 1 y entregue todos los factores de ese nmero. Si el nmero ingresado no es mayor que 1, el programa debe terminar. Recuerda que cualquier nmero tiene al menos como factor al 1.

10. Solucin: 11. int n, factor; 12. /* Preguntamos por el numero */ 13. printf("CALCULO DE FACTORES"); 14. printf("Ingrese un numero mayor que 1 ? "); 15. scanf(%d,&n); 16. /* Verificamos que este en el rango */ 17. if(n>1) 18. { 19. printf ("Los factores son: "); 20. factor=1; 21. /* Hacemos un ciclo probando todos los posibles factores */ 22. while(factor<=n){ 23. if (n%factor==0) 24. printf(%d ",factor); 25. factor++; 26. } 27. /* Indicamos que termina la lista */ 28. printf(".\n"); 29. } 30. else 31. printf("El numero debe ser mayor que 1\n"); Problema 2: Digito verificador del carn de identidad El sistema de identificacin que se usa en Chile sa basa en un cdigo (llamado numero del carn de identidad) que corresponde a un nmero entero (que se le asigna a cada habitante) y un dgito verificador (que se calcula usando el numero anterior) para saber si algunos de los dgitos del carnet han sido modificados. As, para saber si la cdula 12.575.538-1 est correcta, debemos calcular el dgito verificador para 12575538 y compararlo con 1. Si son iguales, el nmero est bien, si no, fue adulterado. Para calcular el dgito verificador se utiliza el siguiente algoritmo:

Se multiplican los digitos del nmero entero, partiendo del ltimo, respectivamente con 2,3,4,5,6,7,2 y 3 Luego se suman todos estos resultados parciales, y que se llamar SUMA. Se calcula el digito verificador como: D = 11 - ( SUMA modulo 11 ) Si D=11 entonces el digito verificador es "0" (cero). Si D=10, entonces el digito verificador es "K". En cualquier otro caso el digito verificador es D.

Solucin

1. int carnet,resultado; 2. /* Preguntamos por el numero */ 3. printf("Ingrese un numero de carnet (mayor que 1) ? "); 4. scanf(%d,&carnet); 5. /* Verificamos que este en el rango */ 6. if(carnet>1) 7. { 8. int factor=2,suma=0,digito=0; 9. /* Hacemos un ciclo que termina cuando carnet sea 0 10. Para rescatar los digitos rescatamos el ultimo y 11. lue go lo quitamos. */ 12. while (carnet>0) 13. { 14. /* Rescatamos es ultimo digito */ 15. digito=carnet%10; 16. /* Quitamos el ultimo digito del numero */ 17. carnet=carnet/10; 18. suma=suma + digito*factor; 19. factor++; 20. if(factor>=8) 21. factor=2; 22. } 23. resultado=11-(suma%11); 24. if(resultado==11) 25. resultado=0; 26. /* Mostramos el resultado */ 27. if (resultado==10) 28. printf("El digito verificador es: K"); 29. else 30. printf("El digito verificador es: %d", resultado); 31. } 32. else 33. printf("El numero debe ser mayor que 1");

Problema 3: Sapo de Micro Un sapo de micro calcula cunto tiempo ha transcurrido entre el paso de dos micros del mismo recorrido. Un Sapo posmoderno usar un Notebook para ayudarse en el clculo. a. Escribe un programa en C que sostenga el siguiente dilogo : A qu hora pas la primera micro (minutos)? 65 A qu hora pas la micro 2? 73 La diferencia entre la 1 y la 2 es de 8 minutos A qu hora pas la micro 3? 78 La diferencia entre la 2 y la 3 es de 5 minutos A qu hora pas la micro 4? -1 Muchas gracias por usar este programa Es decir, que pregunte la hora en minutos. Supn que el programa se usa en un mismo da siempre. b) Escribe el mismo programa, pero la hora se ingresa ahora de la siguiente forma: A qu hora pas la primera micro (minutos)? 1 5 A qu hora pas la micro 2? 1 13 La diferencia entre la 1 y la 2 es de 8 minutos A qu hora pas la micro 3? 2 18 La diferencia entre la 2 y la 3 es de 1 hora 5 minutos A qu hora pas la micro 4? -1 Muchas gracias por usar este programa Es decir, la hora se muestra en horas y minutos Solucin a)

1. int antes,actual,i; 2. printf ("A qu hora pas la primera micro (minutos)?"); 3. scanf(%d,&antes); 4. i=2; 5. while (antes!=-1) { 6. printf ("A que hora paso la micro %d?",i); 7. scanf(%d,&actual); 8. if (actual!=-1) { 9. printf ("La diferencia entre la %d y la %d",i-1,i); 10. printf (" es de %d minutos \n",actual-antes); 11. } 12. antes=actual; 13. i=i+1; 14. } 15. printf ("Muchas gracias por usar este programa\n"); Solucin b) 16. int hora,minutos,antes,actual,i,difh,difm; 17. printf ("A qu hora pas la primera micro (minutos)?"); 18. scanf(%d %d,&hora,&minutos); 19. hora*60+minutos; 20. i=2; 21. while (antes!=-1) { 22. int hora; 23. printf ("A que hora paso la micro %d?",i); 24. scanf(%d %d,&hora,&minutos); 25. actual=hora*60+minutos; 26. if (actual!=-1){ 27. difh=(actual-antes)/60; 28. difm=(actual-antes)%60; 29. printf ("La diferencia entre la %d y la %d es de ",i-1,i); 30. if (difh>0) 31. printf (%d horas y ",difh); 32. printf ("%d minutos",difm); 33. antes=actual; 34. i=i+1; 35. } 36. } 37. printf ("Muchas gracias por usar este programa\n");

Pon tus conocimientos a prueba Escribe un programa que lea las notas de un alumno del teclado y escriba su promedio en la pantalla. Escribe dos versiones; la primera versin pregunta al usuario la cantidad de notas, la segunda versin pregunta notas hasta que se ingresa un 0 para indicar el fin.

Cuntas notas son? 4 Nota 1: 7.0 Nota 2: 6.0 Nota 3: 5.5 Nota 4: 4.0 Promedio: 5.5 Versin 1 Ingresa las notas (0=fin) Nota 1: 7.0 Nota 2: 6.0 Nota 3: 5.0 Nota 5: 0 Promedio: 6.0 Versin 2 Diagramas de flujo Un diagrama de flujo es una representacin grfica del orden en que se van ejecutando las instrucciones de un programa, cmo ya hemos visto de forma superficial en este captulo y el anterior. Hasta ahora hemos visto tres tipos de ejecucin: secuencial, cclica y condicional, cada uno de los cuales tiene un diagrama asociado: Secuencial Condicional Cclica

inst1; inst2; inst3;

if (cond) inst1; else inst2;

while (cond) inst;

Para programas ms complejos, basta con combinar los distintos diagramas. Ejemplo de un diagrama de flujo El siguiente cdigo escribe si un nmero guardado en la variable x es positivo, negativo o igual a cero. A su derecha se ve su diagrama de flujo, que es una composicin de dos diagramas de secuencias condicionales. if (x>0) printf("positivo); else { if (x<0) printf("negativo"); else printf("cero"); }

Los diagramas pueden ayudar a descubrir por qu un cdigo no funciona. Por ejemplo, en el siguiente programa, que intenta hacer lo mismo que el anterior, el diagrama pone de manifiesto cual es el error cometido:

if (x>0) printf("positivo); if (x<0) printf("negativo"); else printf("cero");

En el caso en que el nmero sea negativo, la primera condicin es falsa y se evala la segunda. Como esta es verdadera, se escribe "negativo" en la pantalla. Si el nmero es cero, la primera condicin es falsa, se evala la segunda, que tambin es falsa y se escribe "cero" Si el nmero es positivo, se escribe "positivo". Sin embargo, el error se produce al pasar a evaluar la segunda condicin, que es falsa, por lo que se escribe "cero". Es decir, si el nmero es mayor que cero, se escribe "positivo" y "cero" en la pantalla. Ejecucin paso a paso

La ejecucin paso a paso consiste en escribir el orden en que las instrucciones se van ejecutando al correr el programa. Para ello se le asigna un nmero a cada lnea del programa. Al lado de cada lnea se escribe: Si la instruccin es ... asignacin while o if Se debe escribir el nuevo valor de la variable el valor de verdad de la condicin

Print

qu se escribe en la pantalla

El siguiente programa lee nmeros enteros desde el teclado y escribe cunto vale su suma acumulada. El programa itera hasta que se ingresa un cero. 1. int suma= 0, num; 2. printf("? "); 3. scanf(%d,&num); 4. while (num!=0) { 5. suma= suma+num; 6. printf("Total= %d\n",suma); 7. printf("? "); 8. scanf(%d,&num); 9. } 10. printf("Fin!\n"); La siguiente pantalla muestra un ejemplo: ? 3 Total= 3 ? 4 Total= 7 ? 1 Total= 8 ? 0 Cmo es su ejecucin paso a paso?
(1) suma=0 (2) escribe "?" (3) num=3 (4) Verdadero (5) suma=3

(6) escribe "Total=" (6) escribe "3" (7) escribe "?" (8) num=4 (4) Verdadero (5) suma=7 (6) escribe "Total=" (6) escribe "7" (7) escribe "?" (8) num=1 (4) Verdadero (5) suma=8 (6) escribe "Total=" (6) escribe "8" (7) escribe "?" (8) num=0 (4) Falso (10) escribe "Fin"

Pon tus conocimientos a prueba El siguiente programa calcula el mximo comun divisor entre los nmeros 15 y 24 (Propuesto: demustralo). Dibuja su diagrama de flujo y escribe su ejecucin paso a paso. 1. 2. 3. 4. 5. 6. int x= 15; int y= 24; while (x!=y) { if (x>y) x= x-y; else

7. y= y-x; 8. } 9. printf(%d,x); Solucin:


(1) x=15 (2) y=24 (3) Verdadero (4) Falso (7) y=9 (3) Verdadero (4) Verdadero (5) x=6 (3) Verdadero (4) Falso (7) y=3 (3) Verdadero (4) Verdadero (5) x=3 (3) Falso (9) Escribe 3

For
En C existe una forma abreviada de escribir un ciclo while: la instruccin for. No slo abrevia, sino que ademas aporta claridad en ciertos casos. Su forma es for (instruccin1; condicin; instruccin2) { instrucciones; } equivale a instruccin 1; while (condicin) { instrucciones; instruccin2; } Los siguientes programas muestran ejemplos de la equivalencia entre while y for: Con for... Con while...
Int i; for (i=0; i< 100; i++) printf ("El nmero es %d",i); int i=0; while (i<100) { printf ("El nmero es "+i); i++; } Float tiempo; for (tiempo=0.0;tiempo<10.0;tiempo+=0.1) { printf ("Tiempo = %f,posicion= float tiempo=0.0; while (tiempo<10.0) { printf ("Tiempo = %f,posicion= %f",tiempo,pos);

%f",tiempo,pos); pos=0.1*velocidad; velocidad=velocidad+0.3; }

pos=0.1*velocidad; velocidad=velocidad+0.3; }

Patrones de Programacin
Al escribir un programa no se parte siempre de cero. Muchas veces es posible reutilizar y combinar elementos de programas ya conocidos. Al estudiar diversos programas es posible identificar patrones de instrucciones que se repiten. El concocer diversos patrones nos ayuda a resolver nuevos problemas, pues no tenemos que reinventar soluciones para problemas similares una y otra vez. Patrn de acumulacin Un ejemplo de estos patrones de programacin es la acumulacin. Este patrn se usa para realizar clculos como la suma de varios valores calculados en las iteraciones de un ciclo: suma= val1 + val2 + val3 + ... + valn producto= fac1 * fac2 * fac3 * ... * facn La forma general de este patrn es:
tipo variable = valor inicial; ... while ( ... ) { ... variable = variable operador expresin; ... }

La siguiente pantalla muestra un programa que cuenta cuntos nmeros ingresa el usuario, hasta llegar al nmero cero: ? 4.0 ? 5.0 ? 6.0 ? 0.0 Contador= 3 El programa, en el que aparece claramente el patrn de acumulacin es: 1. 2. 3. 4. 5. int cont= 0; float nota; printf("? "); scanf (%f,&nota); while (nota!=0.0) { /* OJO CON LOS PROBLEMAS DE REPRESENTACIN INTERNA */ 6. cont= cont+1; 7. printF("? "); 8. scanf (%f,&nota); 9. } 10. printf("Contador= "); 11. printf(%d\n,cont); En un programa que calcula el promedio de notas se ve tambin un patrn de acumulacin, cuya diferencia con el anterior es que tiene dos variables que se van acumulando en vez de una sola: ? 4.0 ? 5.0 ? 6.0 ? 0.0 Promedio= 5.0 12. 13. 14. 15. float suma= 0, nota; int cont= 0; printf("? "); scanf (%f,&nota);

16. 17. 18. 19. 20. 21. 22.

while (nota!=0.0) { suma= suma+nota; cont= cont+1; printf("? "); scanf (%f,&nota); } printf("Promedio= %1.1f\n", suma/cont);

Patrn de lectura de datos En el ltimo programa se presenta tambin el patrn de lectura de datos. Su forma general es:
tipo variable; ... leer(variable); while (...) { .... leer(variable); }

Tpicamente se marca el fin de los datos con un cero o -1 (o un nmero que no pertenezca al universo de datos) Patrn de Recorrido de Intervalo de Enteros Si consideramos un programa que despliegue los nmeros enteros de 1 a n, como se ve en la pantalla siguiente, podemos usar el patrn de acumulacin: ? 4 1 2 3 4 El programa termina cuando la variable i excede en valor a n

23. 24. 25. 26. 27. 28. 29. 30.

int n,i; printf("? "); scanf(%d,&n); i= 1; while (i<=n) { printf(%d,i); i= i+1; }

En realidad estamos en presencia de otro patrn muy utilizado: recorrido de un intervalo de enteros. Su forma general es:
int i= valorInicial; while ( i <= ValorFinal ) { ... i= i + 1; }

La variable i toma valores entre [valorInicial, valorFinal], incrementndose en 1 en cada iteracin. Ejemplos Factorial de un nmero Para calcular el factorial de un nmero se agrega el producto de los valores sucesivos que toma la variable i en cada iteracin. Usaremos el patrn de acumulacin con el operador *. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. int fact, n, i; printf("? "); scanf(%d,&n); fact= 1; i= 1; while (i<=n) { fact= fact*i; i= i+1; } printf("El factorial es %d\n", fact);

Exponencial de un nmero usando una aproximacin:

Como se sabe de clculo, exp(x) se puede calcular mediante la siguiente aproximacin: exp(x)= 1 + x + x^2/2! + x^3/3! + ... + x^i/i! + ... + x^n/n! El siguiente programa calcula i! al mismo tiempo que calcula x^i. 41. 42. 43. 44. 45. 46. 47. float xi= 1.0, facti= 1.0; int i= 1; while (i<=n) { xi= xi*x; facti= facti*i; i= i+1; }

Observa que en la i-sima iteracin, la variable xi es x^i y facti es i! 48. A este programa podemos agregarle una acumulacin de xi/facti: 49. float x= ...; /* Obtener x y n */ 50. int n= ...; 51. float xi= 1.0; 52. float facti= 1.0; 53. float expx= 1.0; 54. int i= 1; 55. while (i<=n) { 56. xi= xi*x; 57. facti= facti*i; 58. expx= expx + xi/facti; 59. i= i+1; 60. } 61. printf(%f\n,expx); La instruccin de la lnea 60 simplemente despliega el resultado final Pon tus conocimientos a prueba Escrbe un programa que lea nmeros hasta que el usuario ingrese un cero y diga cuntos nmeros son pares y cuntos son impares, como se ve en la pantalla sigueinte: ?1 ?2

?5 ?2 ?4 ?6 ?9 ?14 ?12 ?8 Hay 7 pares y 3 impares Calculo del mximo de un conjunto de datos Considera un programa que usando el patrn de lectura de datos lea un conjunto de nmeros positivos y escriba el mayor de todos ellos al final: ?1 ?4 ?2 ?1 ?0 El mximo es: 4 Usando algo parecido al patrn de acumulacin, obtenemos 62. 63. 64. 65. 66. 67. 68. 69. 70. int num; int maximo; printf("? "); scanf(%d,&num); maximo= num; while (num!=0) { if (num>maximo) maximo= num; printf("? ");

71. 72. 73.

scanf(%d,&num); } printf("El mximo es %d\n", maximo);

A primera vista puede parecer que este cdigo no usa el patrn de acumulacin, pero si te fijas bien, las lneas 67 y 68 equivalen a maximo=max(maximo, num); que es similar a variable = variable operador expresin; que era parte del patrn de acumulacin. El hipoptamo en el zoolgico de El Cairo Hay un viejo chiste que dice que para buscar un hipoptamo en Africa, el algoritmo que disea un programador inexperto es el siguiente: pararse en Ciudad Del Cabo repetir recorrer hacia el oeste hasta llegar al oceano pacfico si se encuentra un hipoptamo, parar avanzar 1 kilmetro al norte recorrer hacia el este hasta llegar al oceano atlntico si se encuentra un hipoptamo, parar avanzar 1 kilmetro al norte El problema de este algoritmo, es que si no hay ningn hipoptamo el buscador se cae al mediterrneo (JA). El programador experto hace una pequea modificacin al cdigo anterior, al agregar la siguiente instruccin al inicio del programa: poner un hipoptamo en el zoolgico de El Cairo La "moraleja" de este chiste es que siempre hay que tener especial cuidado con las condiciones de borde en las iteraciones. Por

ejemplo, el programa que encuentra el mnimo de un conjunto de datos, puede escribirse de la siguiente forma: 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. int num; int minimo=MAXINT; printf("? "); scanf(%d,&num); while (num!=0) { if (num<minimo) minimo= num; printf("? "); scanf(%d,&num); } printf("El mnimo es %d\n",minimo);

La constante MAXINT sera mayor nmero entero que C puede representar (en realidad hay que hacer un juego con la representacin de los datos para lograrlo). Al asignarselo a minimo, uno asegura que la primera vez que se ejecute la lnea 78 la condicin del if ser verdadera y por lo tanto num se guardar en minimo. Es decir, nos aseguramos que el primer nmero que leemos sea considerado como mnimo, al menos temporalmente. Pon tus conocimientos a prueba Escribe un programa que lea nmeros desde el teclado e indique el rango de los nmeros (el fin de datos es el 0) ?1 ?2 ?5 ?2 ?4 ?0 El rango es [1,5]

De ahora en adelante y hasta el final del curso, iremos introduciendo distintos patrones de programacin. Es importante tener presente qu hace cada patrn y cmo integrarlo a un nuevo programa. Es un excelente ejercicio buscar patrones ya conocidos en tus programas, y mejor an, encontrar patrones nuevos.

Punteros
Un puntero es una variable que contiene la direccin de una variable

Fcil, no?. En realidad no es mas que eso, sin embargo si uno no lo tiene claro es probable que entre en un estado de pnico y confusin debido a que no se sabe si se tiene el valor de la variable o la direccin donde sta se ubica.

Punteros y Direcciones

Veamos la representacin y organizacin de los datos en la memoria primaria (RAM para los amigos). Tpicamente se organizan en celdas contiguas de memoria, con una direccin asignada a cada una de ellas. La idea es poder utilizar una celda o un grupo de ellas de ser necesario.

Comnmente un byte es un char, 2 bytes representan un short integer y 4 bytes representan un integer. Un puntero es un grupo de celdas (en la mayora de los PCs modernos 4) que almacena una direccin de memoria.

El operador unario & asigna la direccin de una variable; por ejemplo, sea c un char y p un puntero, entonces la operacin p = &c

asigna la direccin de c a la variable p, se dice entonces que p apunta a c. Notar que el operador & slo debe ser aplicado sobre variables, jams sobre expresiones, constantes o registros. Se tiene adems el operador * con el cual se obtiene lo apuntado por el puntero en otras palabras, si se tiene lo anterior entonces se cumple que: *p == c

Declaraciones Para declarar un puntero se le debe informar a C que es lo que uno desea almacenar en memoria, por lo tanto se le informa el tipo de lo almacenado, por ejemplo:

char *p; (puntero a char) int *p; (puntero a int) float *p; (puntero a float)

La forma general de la declaracin de un puntero es tipo *variable;

Ejemplo (Pequeo ejercicio mental, si lo entienden van bien encaminados):

int x=1, y=2; int *ip; /* ip es un puntero a int*/

ip = &x; /* ip apunta a la direccin de x */ y = *ip; /* y = 1 */ *ip = 0; /* x = 0 */

Aritmtica de punteros

Una de las gracias de los punteros de C radica en que, al ser manejados como una variable ms, son vlidas todas las operaciones aritmticas de enteros sobre ellos. Lo malo de esto (porque no todo puede ser bueno en la vida) es que el programador puede perder el control sobre ellos dentro de un programa ocasionando ms de un dolor de cabeza a quienes realizan los programas.

Hay un viejo lema que reza: mas peligroso que puntero perdido en C.

Por lo tanto, si ip es un puntero a una direccin de memoria, entonces ip++ har que ese puntero apunte a la direccin siguiente e ip-- har que apunte a la anterior.

ERRORES COMUNES

Se debe tener mucho cuidado al manejar punteros y diferenciar bien entre lo apuntado y el que apunta al utilizar la aritmtica, por ejemplo en el caso anterior en que ip apunta a x se puede tener adems que:

*ip=*ip+10; /* x = 11 se aumenta el valor de lo apuntado */

y = *ip +1 /* y = 12 nuevamente se utiliza lo apuntado*/

Lo entretenido es cuando se tiene:

*ip++;

Se aumenta lo apuntado o la direccin de la memoria?... la respuesta es... DEPENDE DEL COMPUTADOR!!!!!!!! (msica de suspenso de fondo)

As que para evitarse malos ratos se utilizan parntesis:

(*ip)++; /* Aumenta el valor de lo apuntado en 1 */

*(ip++); /* Lo que hace esto es entregar lo apuntado por ip y luego cambiar el puntero a la direccin siguiente */

*(++ip); /* Cambia el puntero a la direccin siguiente y entrega lo apuntado */

Otro error comn al utilizar punteros es creer que por copiar punteros se crea una copia de lo apuntado, por ejemplo:

int x=10; int *px, *py;

px = &x; /* *px = 10 */ py = px; /* *py = 10 */

(*px)++; /* *px = 11 y OJO *py = 11, porque apuntan a la misma direccin*/

cast

Otra de las gracias de C es que uno lo puede engaar hacindole creer que lo que apunta es en realidad otra cosa, a ese engao se le conoce como cast.

Por ejemplo, si uno no sabe el tipo de lo que se apuntar con anterioridad se puede definir como void *, y luego con el cast se le informa al computador el tipo de lo almacenado. Por ejemplo: void *p; /* puntero a cualquier cosa *7 int i; char c;

... /* bla, bla, bla */

if (condicion) { p = (void *) &i; } else { p = (void *) &c; };

... /* ms bla, bla, bla */

if (condicion) { i = (int) *p; } else

En el ejemplo anterior se utiliza el cast (void *) para que se pueda asignar a p la direccin de las variables, y luego se utilizan los casts de los tipos respectivos para recuperar los datos.

Arreglos
Invertir algunos nmeros Considera un programa que lea del teclado 8 nmeros y los despliegue luego en orden inverso, como se ve a continuacin: Ingresa los nmeros Nmero 1: 45 Nmero 2: 54 Nmero 3: 32 Nmero 4: 76 Nmero 5: 32 Nmero 6: 16 Nmero 7: 8 Nmero 8: 7 Los nmeros al revs son: Nmero 8: 7 Nmero 7: 8 Nmero 6: 16 Nmero 5: 32 Nmero 4: 76 Nmero 3: 32 Nmero 2: 54 Nmero 1: 45 La solucin es bastante fcil de programar. Basta con declarar 8 variables enteras, leerlas del teclado y escribirlas en orden inverso:

1. int v1,v2,v3,v4,v5,v6,v7,v8; 2. printf ("Ingresa los nmeros\n"); 3. printf ("Nmero 1:"); 4. scanf(%d,&v1); 5. printf ("Nmero 2:"); 6. scanf(%d,&v2); 7. printf ("Nmero 3:"); 8. scanf(%d,&v3); 9. printf ("Nmero 4:"); 10. scanf(%d,&v4); 11. printf ("Nmero 5:"); 12. scanf(%d,&v5); 13. printf ("Nmero 6:"); 14. scanf(%d,&v6); 15. printf ("Nmero 7:"); 16. scanf(%d,&v7); 17. printf ("Nmero 8:"); 18. scanf(%d,&v8); 19. printf ("Los nmeros al revs son:\n"); 20. printf ("Nmero 8 %d\n",v8); 21. printf ("Nmero 7 %d\n",v7); 22. printf ("Nmero 6 %d\n",v6); 23. printf ("Nmero 5 %d\n",v5); 24. printf ("Nmero 4 %d\n",v4); 25. printf ("Nmero 3 %d\n",v3); 26. printf ("Nmero 2 %d\n",v2); 27. printf ("Nmero 1 %d\n",v1); Qu pasara, sin embargo, si fueran 100 nmeros en vez de slo 8? Habra que declarar 100 nmeros enteros?

Arreglos Un arreglo es un objeto que sirve para almacenar un conjunto de valores del mismo tipo. Su declaracin es: tipoVariable nombreVariable[]; Su creacin es similar a la creacin de un objeto comn, pero se debe especificar adems la cantidad de elementos: tipoVariable nombreVariable[cantidad]; Por ejemplo, si se desea guardar en el arreglo v las votaciones de 8 candidatos la declaracin debe ser hecha de la siguiente manera: int v[8]; v = new int [8]; Los arreglos tienen adems una fuerte relacin con los punteros, puesto que son reas en la memoria a las cuales son apuntados. Por ejemplo el mismo arreglo anterior se puede declarar de la siguiente manera: int *v; v = (int *) malloc (sizeof(int) * 8); Lo que se traduce en la creacin de un objeto de 8 elementos enteros que es apuntado (referenciado por v). Malloc se utiliza para pedir memoria, es decir, C le dice al computador necesito x celdas de memoria, en este caso son 8 celdas para almacenar enteros:

Cada elemento se referencia con el nombre del arreglo y un ndice entero. El rango de los ndices va de 0 a cantidad-1.

Por ejemplo, para hacer referencia al valor del cuarto elemento de v hay que escribir v[3]. El indice dentro de los parntesis cuadrados puede ser cualquier expresin entera que este dentro del rango [0, cantidad-1]. El siguiente cdigo asigna valor 0 a todos los elementos de v: 28. 29. 30. 31. } Como v[i] es un arreglo de enteros, sus elementos pueden ser colocado en cualquier contexto donde sea valido poner una variable de tipo entero. Un par de ejemplos clarifica esto: int v[8]; int a; scanf (%d,&a); i=0; while (i<8) { scanf (%d,&v[i]); i++; } int x=a*2; a=a+1; int x=v[7]*2; v[2]=v[2]+1; i=0; while (i<8) { v[i]=0; i++;

Adems toda la aritmtica de punteros se puede utilizar sobre los arreglos, por ejemplo es vlido: int v[10],x; int *pa; pa = &v[1]; /* pa apunta a la direccin de la celda v[1] */ x = *pa; /* x = v[1] */ x = *(pa+1); /* x = v[2] */ pa = v; /* Correspondencia entre punteros y arreglos*/ /* Es equivalente a pa = &v[0] */ Usando arreglos, el programa para invertir los nmeros queda: 32. 33. 34. 35. 36. 37. 38. 39. 40. int v[8]; printf ("Ingresa los nmeros\n"); for (int i=0; i<8; i++) { print ("Nmero %d:",i); scanf (%d,&v[i]); } printf ("Los nmeros al revs son:\n"); for (int i=7; i>=0; i--) println ("Nmero %d: %d,i,v[i]);

Arreglos de dos dimensiones En un curso de computacin de 100 alumnos se han rendido 6 test a lo largo del ao. Las notas podran guardarse en 6 arreglos de 100 elementos cada uno: float c1[100]; ... float c5100]; float c6[100]; o en una matriz de 100 x 6 double notas[100][6]; Como ejemplo, si los datos se encuentran en la matriz: notas 6.7 6.5 6.3 6.1 6.8 7.0 1.2 2.3 3.1 4.0 4.0 2.1 4.5 4.4 4.1 4.0 4.0 3.5 ... El promedio de cada alumno es simple de calcular: 41. for (alumno=0; alumno<100; alumno++) { 42. float suma=0.0; 43. int test; 44. for (test=0; test <6; test++) 45. suma=suma+notas[alumno][test]; 46. println ("El promedio del alumno %d es %1.2f",alumno+1,suma/6); 47. }

Filas o columnas? Una de las preguntas ms comunes en arreglos bidimensionales es cul indice representa las columnas y cul las filas. Por ejemplo, si se te pide crear un arreglo para guardar las notas de 100 alumnos a lo largo de 5 pruebas, cul de las siguientes declaraciones es la correcta? float nota[100][5]; o float nota[5][100]; En realidad da lo mismo, slo importa ser consecuente (aunque ser consecuente no es una virtud) despus de la declaracin y respetar la convencin que uno mismo se da. El cdigo de la figura 1 es correcto, a diferencia del de la figura 2, que no respeta el sentido de la declaracin: 1. 2. 3. 4. 5. 6. float nota[100][5]; for (i=0; i<100;i++) for (j=0; j<5; i++) { print ("Ingresa la nota %d del Alumno %d,j,i); scanf (%f",&nota[i][j]); } Figura 1 7. float nota[5][100]; 8. for (int i=0; i<100;i++) 9. for (int j=0; j<5; i++) { 10. print ("Ingresa la nota %d del Alumno %d,j,i); 11. scanf (%f",&nota[i][j]); 12. }

Figura 2 El cdigo de la figura 2 corregido es 13. 14. 15. 16. 17. 18. float nota[5][100]; for (int i=0; i<100;i++) for (int j=0; j<5; i++) { print ("Ingresa la nota %d del Alumno %d,j,i); scanf (%f",&nota[j][i]); } Asignacion de arreglos Imagina que tienes un arreglo A de 100 nmeros de tipo entero y quieres copiarlo en un arreglo B. El cdigo que escribira un alumno de pocas luces es: 19. 20. 21. 22. int A[100], B[100]; /* leer A de el teclado, o calcularo */ ... B=A;

Este cdigo compila, pero qu es lo que hace? . En este caso estamos creando un objeto que consiste de 100 elementos enteros y guardamos una referencia a l en A. En la segunda declaracin se le dice a C que en B se guardar una referencia a un arreglo de enteros. En la lnea 22 se hace que B haga referencia al mismo objeto (arreglo de enteros) que A:

En todo caso, el compilador de C reclamar porque dir que B es esttico y no dinmico (averigue la diferencia... investigue, no sea flojo), lo cual se mejorara si se declara B como int *B , sin embargo la solucin an no es correcta. El siguiente es un ejemplo erroneo de querer usar B para guardar una "copia" de A para luego reestablecer los valores originales: 23. 24. int A[100], *B; ....

25. 26. 27. 28. 29. 30.

B=A; /* guardamos copia de A */ /* se modifica A */ for (int i=0; i<100;i++) A[i]=...; /* reestablecemos los valores de A */ A=B;

Cualquier cambio hecho en el arreglo en la lnea 28 es permanente, en el sentido que la linea 30 no tiene ningn efecto, pues A y B ya hacen referencia al mismo objeto a partir de la lnea 25. El cdigo correcto es 31. int A[100], B[100]; 32. /* o bien: int *B; B = (int *) malloc (100*sizeof(int));*/ 33. ... 34. for (int i=0;i<100;i++) 35. B[i]=A[i]; 36. // se modifica A 37. for (int i=0; i<100;i++) 38. A[i]=...; 39. // reestablecemos los valores de A 40. for (int i=0;i<100;i++) 41. A[i]=B[i]; El siguiente diagrama muestra la situacin despus de la lnea 31:

Patrnes en arreglos El patrn ms comn es el de recorrer un arreglo:


int i=0; while (i<largo(A)) {

... A[i] ... }

Similar es el patrn de recorrido de un arreglo en orden inverso:


int i=largo(A); while (i>=0) { ... A[i] ... i++; }

El patrn de bsqueda de un elemento es un caso particular del recorrido:


boolean encontrado=false; int i=0; while (i< largo (A)) { if (A[i] igual a elemento_buscado) { encontrado=true; break; } i++; }

El patrn de bsqueda del ndice de un elemento es muy usado:


int indice=-1; int i=0; while (i< largo(A)) { if (A[i] igual a elemento_buscado) { indice=i; break;

} i++; } /*si indice==-1 entonces el elemento no existe */

Recorrido de una matriz Existen varias formas de recorrer un arreglo, dependiendo de la forma en que se incrementen los ndices. Hay veces en las que la forma del recorrido no tiene ninguna importancia (sumar todos los datos de la matriz), pero en problemas como resolver un sistema de ecuaciones lineales s importa:

for (fila=0;fila<n; fila++) for (col=0; col<m; col++) ... A[fila][col];

for (fila=0;fila<n; fila++) for (col=m-1; col>=0; col--) ... A[fila][col];

Pon tus conocimientos a prueba

Cmo haras para almacenar una matriz de m x n elementos en un arreglo de una sola dimensin? Escribe un programa que multiplique matrices

Problemas resueltos Problema 1: Matriz simtrica Muchos problemas de algebra lineal son simplificables si las matrices involucradas cumplen ciertas propiedades. Dentro de las tantas propiedades que puede tener una matriz, es ser simtrica.

Un ejemplo de matriz simtrica es ste:

Es decir, si A es matriz de n x n, entonces A es simtrica si:

Escriba un programa en Java que determine si una matriz es simtrica o no. Suponga que ya tiene una matriz A de tamao n x n. Tenga cuidado con la solucin, aproveche las propiedades de la matriz para resolver el problema eficientemente. 1. /* En este caso, es solo un TROZO de cdigo 2. Se tienen la matriz A de tamano nxn y los indices que sean 3. necesarios. Usaremos una variable flag, que registra un 4. estado como interruptor */ 5. int A[n][n]; 6. int i,j,flag=true; 7. for(i=0; i<n; i++) 8. for(j=0; j<i; j++) 9. if (A[i][j] != A[j][i]) 10. flag=false; 11. if (flag) /* Es simetrica, no cambio nunca */ 12. printf("Matriz simetrica\n"); 13. else 14. printf("Matriz no simetrica\n");
Problema 2: Consultora sentimental

Una empresa de consultora sentimental se encarga de poner en contacto personas que podran tener afinidad para que se conozcan. Para esto tienen un programa en C (de miles de lneas de cdigo) que se encarga de procesar toda la informacin sobre los clientes para encontrar quienes podran entablar una buena relacin. El programa entrega como resultado, un arreglo de tamao NPERSONAS (que es un int) de enteros (compatible[ ]) que es un ndice para el arreglo de personas que indica la persona con que tiene afinidad (No se almacena el nombre para mantener el anonimato). Por ejemplo, si compatible[10]=25 quiere decir que la persona 10 le agradaria la persona 25, pero no necesariamente al revs.

Para terminar el programa, falta escribir las partes que permitan hacer ciertas consultas: a. Escribe la parte del programa que permita encontrar todas las parejas mutuamente compatibles. No importa que la pareja aparezca repetida. b. Escribe la parte del programa que encuentre la persona con quien mas personas son compatibles. Sin es ms de una imprima cualquiera. c. Escribe la parte del programa que encuentre aquellas personas con quienes nadie es compatible (snif). Indicacion: Suponga que los arreglos nombre[ ] y compatible[ ] existen y ya tienen la informacin. Solucin a) 1. 2. 3. 4. 5. 6. /* Revisamos todas las personas */ int i; for(i=0; i<NPERSONAS; i++) /* Si una persona es comptible con otra que es compatible con la primera, son mutuamente compatibles */ if( i==compatible[compatible[i]]) {

7. printf("%d y %d son compatibles y hacen pareja\n"i,compatible[i]); 8. } Solucin b) 9. int maxcomp=0; /* Guarda la compatibilidades del maximo */ 10. int ncomp; /* Guarda el No. de compat. de la persona actual */ 11. int indmaxcomp=0; /* Indice del mas compatible */ 12. /* Recorremos todas las personas */ 13. int i; 14. for(i=0; i<NPERSONAS; i++) 15. { 16. int j; 17. ncomp=0; 18. for(j=0; j<NPERSONAS; j++) 19. /* Chequeamos si es compatible */ 20. if (compatible[j]==i) 21. ncomp++; 22. /* Si tenemos un nuevo maximo */ 23. if(ncomp>maxcomp) 24. { 25. maxcomp=ncomp; 26. indmaxcomp=i; 27. } 28. } 29. printf("Con %d son compatibles %d personas",indmaxcomp,maxcomp); Solucin c) 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. /* Chequeamos todas las personas */ int i; for(int i=0; i<NPERSONAS; i++) { ncomp=0; /* Revisamos si tiene alguien compatible */ int j; for(j=0; j<NPERSONAS; j++) // Si tiene alguien compatible terminamos if(compatible[j]==i) { ncomp=1; break; }

44. 45. 46.

if (ncomp==0) printf("Nadie es compatible con %d\n",i); }

Memoria Dinmica
La funcin bsica es malloc, que reserva unr espacio de memoria del tamao que se le indique (en bytes) y retorna un puntero. Siempre hay que anteponerle un cast a la llamada, para que el tipo del puntero retornado se adapte a lo que se espera.
#include <stdlib.h> char *s; s = (char *)malloc(100); /* reserva un rea de 100 bytes */

Para pedir espacio para un arreglo hay dos alternativas. Una es usar malloc y calcular "a mano" el espacio total requerido. Por ejemplo,
p = (int *)malloc(100*sizeof(int)); /* p apunta a un arreglo de 100 int's */

La otra forma es usar otra funcin, calloc, que tiene dos parmetros (nmero de celdas y tamao de cada celda):
p = (int *)calloc(100, sizeof(int)); /* p apunta a un arreglo de 100 int's */

Un efecto lateral de calloc es que inicializa en cero la memoria que entrega. Al terminar de usar un rea de memoria pedida dinmicamente, se le debe retornar al sistema usando
free(p);

donde p apunta al principio del rea que se libera. En C no hay recoleccin automtica de basura, es responsabilidad del programador liberar la memoria que ya no use. Los programas que funcionan durante mucho tiempo y que van olvidando liberar memoria se dice que tienen un error de tipo "memory leak", el cual puede ser muy difcil de investigar.

Strings
Un String, escrito como Hola, soy un string es en realidad un arreglo de caracteres. En la representacin interna, el arreglo es terminado con el carcter nulo 0 (o bien '\0'); as lo sprogramas sabes cual es su final. Por lo tanto el tamao ocupado en el almacenamiento es siempre un carcter ms que el largo del string en s. Existe una gran diferencia entre las siguientes definiciones: char astring [] = definitivamente soy string; /* un arreglo */ char pstring = definitivamente soy string; /* un puntero */ astring es un arreglo, del tamao definido slo por el mensaje y el '\0', esttico e inamobible. Tal vez se puedan modificar algunos caracteres del mensaje, pero cada referencia a astring se referir siempre a la misma direccin de memoria que tiene reservada la misma cantidad de espacio. Por otro lado pstring es un puntero, inicializado para apuntar a un arreglo de caracteres; como buen puntero uno lo puede mover a la direccin de memoria que desee, sin embargo no se pueden modificar los caracteres del string declarado estticamente. C posee una librera (string.h) con funciones para el tratamiento de los strings, las ms comunes las veremos a continuacin.

strcpy(char *destino, char *fuente)


Esta funcin copia todos los caracteres del string fuente a la direccin de memoria destino (obviamente destino debe tener asignado un espacio en memoria donde quepa el string fuente) desde el primer carcter apuntado por destino hasta la primera aparicin del carcter nulo (\0). Implementado con arreglos se vera as: { int i=0; while ((destino[i] = fuente[i]) != 0) i++; } Explicacin:
o

Copia el carcter i-simo de destino en el espacio i-simo de fuente.

Mientras lo que ha copiado no sea el carcter nulo, aumenta el contador i en uno.

Por otro lado, la misma funcin implementada con aritmtica de punteros es as: { while ((*destino = *fuente) !=0) { destino++; fuente++; }; }; La explicacin es la misma. Ahora si quieren ver realmente si entendieron punteros, que les parece el siguiente cdigo que hace lo mismo: { while (*(destino++) = *(fuente++)); }; Nota: El valor 0 es considerado como FALSO para una condicin.

int strcmp(char *s1, char *s2)


Esta funcin se utiliza para comparar dos strings, devolviendo el valor de comparar s1 y s2. Claramente si retorna 0 significa que los dos strings son iguales. La implementacin de esta funcin utilizando aritmtica de arreglos es: { int i; for (i=0; s1[i] == s2[i]; i++) if (s1[i] == 0 && s2[i]==0) return 0; return s1[i] - s2[i];

}; La explicacin es simple:
o o o

Inicializo el contador en 0; Comparo letra por letra los strings, si es el carcter nulo quiere decir que ambos son iguales. Retorno la diferencia entre ellos al momento de salir del ciclo (cuando ambos caracteres son distintos).

Ahora la versin con punteros del mismo es: { for ( ; *s1 == *s2; s1++, s2++) if (*s1 == 0 && *s2 == 0) return 0; return *s1 - *s2; }

int strlen (char *s)


Entrega el largo en caracteres de un string s.

strcat (char *s1, char *s2)


Copia el string s2 al final del string s1.

strncpy (char *s1, char *s2, int n)


Copia los primeros n caracteres de s2 a la direccin de memoria apuntada por s1.

Ejercicio:
o

Implementar las 3 funciones anteriores utilizando lgica de arreglos y lgica de punteros.

Por ltimo, para leer slo un carcter desde el teclado se utiliza la funcin getchar de la siguiente manera: char c; ..c=getchar();

Estructuras
Una estructura es una agrupacin de un conjunto de campos. de igual o distinto tipo; su forma tpica es: struct nombre { tipo_1 variable_1; tipo_2 variable_2; ... tipo_n variable_n; } Los tipos utilizados pueden ser los bsicos (char, int, float) o bien punteros u otras estructuras. Ejemplo: /* Definicin de la estructura */ struct punto { float x; float y; }; /* Declaraciones de variables */ struct punto u, v; struct punto z={1.0, -2.5}; /* Acceso a componentes */ z.x=2.0;

printf("z=(%f, %f)\n", z.x, z.y);

Punteros a Estructuras
struct punto *p; p=&z; printf("%f\n", (*p).x); printf("%f\n", p->x); /* notacin alternativa ms simple */

Problema para resolver:


Haga un programa que reciba una palabra (string) y vea si es palndrome (si la leen hacia adelante dice lo mismo que si la leen hacia atrs) o no.

FUNCIONES
Motivacin Escribe un programa que determine la cantidad de combinaciones que se pueden realizar tomando k elementos de un grupo de n elementos, es decir,

Por ejemplo,

La solucin es 1. int i, n, k , nfactorial=1, kfactorial=1, nkfactorial=1; 2. int combinaciones; 3. scanf (%d %d,&n,&k); 4. for (i=1; i<n; i++) 5. nfactorial=nfactorial*i; 6. for (i=1; i<k; i++) 7. kfactorial=kfactorial*i; 8. for (int i=1; i<n-k; i++) 9. nkfactorial=nkfactorial*i; 10. combinaciones=nfactorial/(kfactorial*nkfactorial); 11. printf ("El nmero de combinaciones es %d\n",combinaciones"); Si te fijas, las lneas 4-5, 6-7 y 8-9 hacen practicamente lo mismo: calcular el factorial de un nmero (n, k y n-k respectivamente). C permite definir funciones, que son subprogramas que llevan a cabo tareas especficas. A modo de ejemplo, si suponemos que existe una funcin de nombre factorial que recibe un nmero entero y devuelve su factorial, entonces el programa queda 1. int n,k,combinaciones; 2. scanf (%d %d,&n, &k);

3. combinaciones=factorial(n)/(factorial(k)*factorial(nk)); 4. printf ("El nmero de combinaciones es %d\n",combinaciones"); El programa no slo ha quedado mucho ms pequeo, sino que adems es ms fcil de entender. la funcin factorial El problema ahora es escribir la funcin factorial: 1. 2. 3. 4. 5. 6. 7. int factorial (int x) { int producto=1, i; for (i=1; i<=x; i++) producto=producto*i; return producto; } Explicacin El encabezado de la funcin especifica qu devuelve la funcin, el nombre de la funcin y que es lo que recibe. En este caso, la funcin se llama factorial, recibe un entero x y su resultado es un entero. Se calcula el factorial con un ciclo for. La variable auxiliar producto contiene el valor. El resultado de la funcin factorial es producto 5

Lnea(s)

7 a 11 11

8. El programa completo queda as: 9. int factorial (int x) 10. { 11. int producto=1, i; 12. for (i=1; i<=x; i++) 13. producto=producto*i; 14. return producto; 15. } 16. void main() 17. { 18. int n, k, comb; 19. scanf (%d %d,&n, &k); 20. comb=factorial(n)/(factorial(k)*factorial(n-k)); 21. printf ("El nmero de combinaciones es %d\n",comb); 22. } 23. }

Forma general de una funcin Una funcin consta de dos partes principales: su encabezado y su cuerpo. El encabezado especifica en forma precisa el nombre de la funcin, qu parmetros recibe y qu devuelve. int tipo factorial nombre (int x) (parmetros)

El cuerpo de la funcin lleva a cabo el "trabajo sucio" y tiene como restriccin que tiene que devolver un valor cuyo tipo sea el especificado en el encabezamiento. Suele terminar con la instruccin return. Esta instruccin devuelve el valor de la expresin que le acompaa y termina la ejecucin de la funcin. En la figura de la derecha se esquematiza lo que hace una funcin: recibe parametros y devuelve un resultado. Para el usuario dla funcin lo nico necesario es saber qu hace la funcin, que devuelve y que recibe, el cdigo del cuerpo dla funcin no le interesa. La figura muestra algo que no debes olvidar: a pesar de que un funcin puede tener varias entradas, solo arroja un resultado. No puedes escribir un funcin que devuelva ms de un resultado; en la prctica se puede devolver una referencia a un objeto (como un arreglo) que dentro tenga varios valores.

Invocacin de un funcin

En la lnea 23 se invoca al funcin factorial. La forma general de invocacin a un funcin es factorial nombre (k) expresin(es)

Cmo la funcin devuelve un valor, hay que asignarlo a una variable o usarlo dentro de una expresin: int total=factorial(10); printf ("El factorial de 5 es %d\n,factorial(5)); Qu hace C al ver una invocacin? Consideremos el ejemplo: int x=factorial(n-k);

1. Se evalu las expresiones que se pasarn como parmetro. Si n=5 y k=3, entonces se calcula n-k=2 2. Se copia el valor de la expresin en el parmetro correspondiente dla funcin 3. Se ejecutan las instrucciones de la funcin 4. Se recibe el resultado de la funcin en el punto de invocacin

Tiene que haber correspondencia entre los valores que se le entregan al funcin y la declaracin de los parmetros de la funcin.

Considera la funcin char *nombreRey (char *nombre, int repeticion) Esta funcin recibe un nombre y "nmero" de un rey y devuelve su nombre con el nmero romano: char *rey=nombreRey ("Enrique",8); printf (rey); // escribe Enrique VIII

Es erroneo escribir char *rey=nombreRey (8,"Enrique") pues la funcin nombreRey espera que el primer parmetro sea una expresin de tipo String. funciones void Los funciones que no devuelven un valor se declaran como void (vacos): 1. void dibujarRectangulo(int x, int y) 2. { 3. char lineaHor[80],borde[80]; 4. int i; 5. lineaHor[0]=0; 6. for (i=0; i<x; i++) 7. strcat(lineaHor,"*"); 8. strcpy(borde,"*"); 9. for (i=1; i<=x-2; i++) 10. strcat(borde," "); 11. strcat(borde,"*"); 12. printf (%s\n,lineaHor); // *********** 13. for (int v=1; v<y-2;v++) 14. printf (%s\n,borde); // * * 15. printf (%s\n,lineaHor); // *********** 16. }

Como no se devuelve ningn valor, tampoco est la instruccin return. El nico contexto en el cual se utiliza return en un funcin void es cuando uno quiere terminar la ejecucin dla funcin si se cumple alguna condicin: 17. void dibujarRectangulo(int x, int y) 18. { 19. if (x>80 || y>25) /* dimensiones muy grandes? */ 20. return; /* no se dibuja nada */ 21. char lineaHor[80]; 22. ...

Forma general de un programa Un programa puede ser visto como un conjunto de funciones que trabajan en forma conjunta para llevar a cabo un objetivo. La forma del programa es #include .... tipo nombreFuncion1 (parmetros) { ... } tipo nombreFuncion2 (parmetros) { ... } tipo nombreFuncion3 (parmetros) { ... } void main() { ... invocaciones a los distintos funciones ... } El siguiente programa es un ejemplo de programa con ms de un funcin.

23. int factorial (int x) 24. { 25. int producto=1,i; 26. for (i=1; i<=x; i++) 27. producto=producto*i; 28. return producto; 29. } 30. int combinatoria(int n, int k) 31. { 32. int resultado=factorial(n)/ (factorial(k)*factorial(n-k)); 33. return resultado; 34. } 35. void main() 36. { 37. printf ("Ingresa la cantidad total de elementos\n"); 38. scanf(%d,&n); 39. printf ("Ingresa cunatos elementos hay que escoger\n"); 40. scanf(%d,&k); 41. printf ("Se pueden escoger de %d maneras",combinatoria(n,k)); 42. } Si te fijas, la funcin combinatoria a su vez llama a la funcin factorial. Una funcin siempre puede llamar a otra funcin, no hay restricciones. En el siguiente captulo veremos que incluso se puede llamar a s mismo. 43. Paso de parmetros 44. El paso de los parmetros en C es por valor, es decir, la expresin en la invocacin de la funcin se copia en el parmetro correspondiente de la funcin. 45. Esto quiere decir que si la variable de la funcin se modifica la variable en la invocacin permanece inalterada. 46. Qu hace el siguiente programa? 47. void incrementa(int x, int cuanto) 48. { 49. x=x+cuanto; 50. } 51. void main() 52. { 53. int p=5; 54. incrementa(p,3); 55. printf (%d\n,p); 56. }

La respuesta es que escribe 5 en la pantalla, y no 8 como podra suponerse. Sigamos la ejecucin del programa para ver esto claramente: Lnea(s) 49 Ejecucin Se le asigna 5 a p p __5__ 50 43 Se llama a incrementa con los valores (5,3) El valor 5 se copia en una nueva variable x. El valor 3 se copia en cuanto x __5__ p __5__ 45 x se incrementa en 3 x __8__ p __5__ 51 se escribe el valor de p, que es 5

Cmo sera el programa anterior escrito correctamente? 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. int incrementa(int x, int cuanto) { x=x+cuanto; return x; } void main() { int p=5; p=incrementa(p,3); printf (%d\n,p); }

Los parmetros no estn limitados a los tipos int, double y float. Tambin se pueden pasar referencias a estructuras. Arreglos como parmetros Un arreglo, al ser un objeto, puede pasarse como entrada a una funcin, basta con pasar una referencia al arreglo 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. float promedio (float A[], int largo) { double suma=0.0; int i; for (i=0; i < largo;i++) suma=suma+A[i]; return suma/largo; } void main() { int n; float *notas,p; printf ("Cuntas notas"); scanf(%d,&n); notas = (float *) malloc (sizeof(float)*n); for (int i=0;i<n;i++) scanf(%f,&notas[i]); p=promedio(notas,n); printf ("El promedio es %1.2f",p); }

Otro ejemplo es un funcin que calcule el mximo valor de un arreglo:

88. 89. 90. 91. 92. 93. 94.

float mximo(float A[], int largo) { float m=A[0]; for (int i=1; i<largo ;i++) if (m<A[i]) m=A[i]; return m; }

Arreglos como parmetros: 95. void suma (float C[], float A[], float B[], int largo) 96. { 97. int i; 98. for (i=0; i< largo;i++) 99. C[i]=A[i]+B[i]; 100. } 101. void main() 102. { 103. float A[3],B[3],F[3]; 104. A[0]=3; A[1]=4; A[2]=1; 105. B[0]=4; B[1]=5; B[2]=1; 106. suma(F,B,A,3); /* en F queda (7,9,2) */ 107. } Sin embargo, si la funcin suma fuera 108. { 109. 110. 111. 112. void suma (float *C, float *A, float *B, int largo) C=(float *) malloc (sizeof(float)*largo); for (int i=0; i<largo ;i++) C[i]=A[i]+B[i]; }

Invocar la funcin suma no tendra ningn efecto en el programa principal Por qu? La razn es que lo que se le pasa a la funcin suma es una referencia al arreglo F. En la lnea 105 a C se le asigna una nueva referencia a un arreglo recin creado. En las lnea 107 se trabaja sobre este nuevo arreglo. Al retornar a la lnea 108, el arreglo referenciado por F no se ha tocado. Es ms, el nuevo arreglo creado en 105 se ha perdido, pues no se tiene ninguna referencia a l en el programa principal. Podra hacerse creando un nuevo arreglo dentro de la funcin y devolviendo una referencia a l, pero slo con memoria dinmica, no esttica:

113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126.

float *suma (float A[], float B[], int largo) { float *C=(float *) malloc (sizeof(float)*largo); for (int i=0; i<largo ;i++) C[i]=A[i]+B[i]; return C; } void main () { float A[3],B[3], *F; A[0]=3; A[1]=4; A[2]=1; B[0]=4; B[1]=5; B[2]=1; F=suma(B,A); } La funcin main

A esta altura, resulta evidente que el "main" que uno escribe en el programa es un funcin ms. En su declaracin se especifica que no recibe parmetros y que tampoco devuelve nada... Pero en realidad SI LOS RECIBE!!!!! (chachachachaaaan!!!!) La verdadera declaracin de main es: main(int argc, char *argv[]) El arreglo argv tiene argc elementos, los cuales son punteros a los strings que son las palabras de la lnea de comandos, comenzando por argv[0], que es el nombre del ejecutable. Por ejemplo, sea el programa echo.c escrito como:

main(int argc, char *argv[]) { int i; for(i=1; i<argc; ++i) printf("%s ", argv[i]); } Al compilar y luego ejecutar este programa de la forma:

% gcc -o echo echo.c % echo hola y chao Los valores de los parmetros de entrada son:

Pon tus conocimientos a prueba

Escribe un funcin que reciba un String y lo devuelva invertido

Errores (demasiado) comunes Los errores ms comunes estn relacionados con el paso y devolucin de valores a funciones: No hacer nada con el valor devuelto por un funcin:

int n; scanf(%d,&n) factorial(n); Lo correcto es int n; scanf(%d,&n); printf (%d,factorial(n)); o int n,f;

scanf(%d,&n) f=factorial(n); printf (%d,f); Tratar un funcin void como si retornara un valor printf ("El rectangulo que usted desea es %s\n",dibujarRectangulo(h,v)); Lo correcto es printf ("El rectangulo que usted desea es\n"); dibujaRectangulo(h,v); Poner los [ ] al llamar a un funcin que recibe arreglos float notas[100],p; ... p=promedio(notas[100]);

lo correcto es: p=promedio(notas); Leer los parmetros dentro de la funcin Este es sin duda el error ms comun y el conceptualmente ms grave. Consiste en leer del teclado los valores de los parmetros de un funcin, siendo que estos se reciben del program principal: int suma(int a, int b) { scanf(%d,&a); scanf(%d,&b); return a+b;

} Lo correcto es leer los datos (si es que son ledos) en la funcin principal int suma(int a, int b) { return a+b; } void main () { int a,b; scanf(%d,&a); scanf(%d,&b); printf (%d\n,suma(a,5)); }

Problema propuesto: Analice las siguientes funciones para asignar memoria a un puntero y vea si son correctas o no. void f(int *p, int n) { p = (int *) malloc (sizeof (int) *n); } int *f (int *p, int n) { p = (int *) malloc (sizeof (int) * n); return p; } int *f (int n) { int *p; p = (int *) malloc (sizeof (int) * n); return p; }

Archivos
Un vendedor desea crear una base de datos con los productos que vende, las unidades disponibles y sus precios, con el propsito de no vender productos que no estn en bodega y no vender a precios equivocados.

El lugar lgico en donde almacenar esta base de datos es en un archivo en disco. Esta informacin no se puede tener permanentemente en la memoria del computador, porque i. ii. se borra cuando se apaga el computador, y su tamao es limitado (tpicamente unos 64 megabytes, que equivalen a 1024*1024 bytes).

En cambio, el disco permite almacenar datos en forma (i) permanente o persitente (no se borra al apagar el computador), y (ii) masiva (tpicamente unos 4 gigabytes, que equivalen a 4096 megabytes).

Desventaja del disco: la manipulacin de datos en disco es mucho ms lenta que en la memoria del computador.

Archivos Los computadores almacena la informacin permanente en dispositivos de almacenamientos, tales como discos duros, diskettes y discos compactos. La informacin almacenada puede ser muy variada, textos, aplicaciones (programas), imgenes o sonidos. Sin embargo, todo esta informacin se guarda en elementos llamados "archivos".

El tamao de un archivo puede ir desde 0 bytes (un archivo vaco) hasta archivos de varios gigabytes. Dependiendo del sistema operativo, estos archivos se organizan en estructuras similares a rboles llamados directorios. Cada archivo se identifica mediante un nombre. Archivos de texto Los archivos de texto son el tipo ms simple de archivo. Estn compuestos por lneas de texto, cada lnea contiene palabras (o smbolos) separadas por espacios. Uno puede representar un archivo de texto como un conjunto de lneas de ancho variable: Puro Chile es tu cielo azulado puras brisas ...

En un archivo de texto se pueden guardar distintas cosas:

canciones fuentes de programas bases de datos simples diarios etc

Un editor de texto es un programa que permite leer y modificar un archivo de texto. Por ejemplo, Notepad de Windows es un editor de texto. Los archivos de texto se caracterizan por ser "planos", es decir, todas las letras tienen el mismo formato y no hay palabras subrayadas, en negrita, o letras de distinto tamao o ancho.

Los archivos binarios, a diferencia de los archivos de texto, guardan informacin que no se puede representar por letras: imgenes o texto con formato. Al abrir estos archivos con un editor de texto, el editor se llena de smbolos extraos, pues trata de interpretar la informacin dentro del archivo de una forma incorrecta.

Las aplicaciones (ejecutables), tambin se almacenan en archivos, como secuencias de unos y ceros. Tampoco puede mirarse su contenido con un editor de texto.

Escritura de archivos En este curso crearemos archivos mediante fopen y fclose, y el tipo de variable FILE *, funciones de la librera stdio.h. Por ejemplo, consideremos un programa que escribe un archivo con nombres de productos que ingresa el usuario. El dilogo entre programa y usuario ser:

producto ? jabon Lux producto ? pasta de dientes Odontine producto ? ... El programa debe escribir el archivo "productos.txt" de tal forma que su contenido sea el siguiente: Archivo: productos.txt jabn Lux pasta de dientes Odontine champ Sedal

...

El programa que realiza este dilogo y escribe el archivo es el siguiente:

1. FILE *F; 2. char producto[20]; 3. F = fopen (producto.txt,w); 4. printf("Ingrese el nombre de un producto: "); 5. scanf(%s,producto); 6. while (strcmp(producto,"fin")!=0) 7. { 8. fprintf (F,%s,producto); 9. printf("Ingrese el nombre de un producto: "); 10. scanf(%s,producto); 11. } 12. fclose(F); En la lnea 1 el programa fabrica/construye un puntero a una estructura donde almacenar la informacin de ``productos.txt''. Se indica como argumento el nombre del archivo que se va a escribir y que el tipo de archivo ser de escritura (si hubiera sido r sera lectura). En la estructura tipo FILE y cuyo nombre en este ejemplo es F queda la informacin del archivo. Para escribir en el archivo se utiliza la funcin fprintf, de la misma forma en que se utilizaba printf slo que a sta funcin se le asigna un archivo como primer parmetro. por tanto, para leer un archivo se utilizar fscanf al igual que nuestra conocida scanf, siempre que el archivo sea abierto para lectura. fprintf no escribe los datos de inmediato en el disco, si no que espera a formar un lote grande y luego escribirlos en conjunto. Esto resulta ms rpido que escribirlos de a uno, porque escribir un byte en disco toma el mismo tiempo que escribir 4 kilobytes. A eso se le conoce como BUFFER. Si se desea escribir inmediatamente a disco, despus de abrir el archivo se debe utilizar la funcin fflush (FILE *). Funcin fclose(FILE *) fclose(F) ordena a escr concluir la escritura del archivo (si hay un lote pendiente lo escribe) referenciado en F. Despus de invocar fclose(F) no es posible volver a escribir utilizando F, a menos que se le asigne otro archivo. La improtancia de fclose radica en que el archivo no ser creado hasta que esta funcin no se ejecute.

Lectura de un archivo Se desea escribir un programa que despliegue en pantalla los productos almacenados en el archivo "productos.txt''. El programa es: 1. 2. 3. 4. 5. 6. 7. 8. 9. FILE *F, char linea[64]; F=fopen(producto.txt,r); fgets(linea,64,F); while ( !feof(F) ) { printf(linea); fgets(linea,64,F); } fclose(F);;

Funcin fgets(char *linea, int max, FILE *F) Como se habrn dado cuenta no utilizamos para este ejemplo la funcin fscanf sino que la funcin fgets, sto porque la primera lee palabra por palabra y la segunda lnea por lnea. Los parmetros de fgets son: el string donde se dejar lo ledo, el mximo de carcteres que se leern del archivo (si la lnea del archivo tiene menos que max entonces se marca en linea con el carcter '\0') y el puntero a la estructura del archivo. IMPORTANTE: Si se ha producido un EOF (End of File, o lectura de la marca de final de archivo) entonces fgets retorna NULL. Funcin feof(FILE *F) feof(FILE *F): consulta si se alcanz el final del archivo (end of file). Esta operacin retorna un valor entero y por lo tanto se coloca como condicin en un if o un while. Retorna verdadero (1) cuando la ltima operacin de lectura se hizo ms all del final del archivo. Pon tus conocimientos a prueba Escribe un programa que dialogue con el usuario para escribir un archivo "productosprecios.txt'' que incluya producto y precio en cada lnea. El programa debe aprovechar los productos almacenados en "productos.txt'' para que el usuario no tenga que digitarlos nuevamente. El dilogo debe ser el siguiente: Precio de jabon Lux ? 344 Precio de pasta de dientes Odontine ? 415 ...

El archivo productos-precios.txt escrito debe ser: jabn Lux 344 pasta de dientes Odontine 415 champ Sedal 507 ...

Solucin:

1. FILE *PROD, *PRODPRE; 2. char linea[64]; 3. int precio; 4. PROD = fopen(producto.txt,r); 5. PRODPRE = fopen (productos-precio.txt,w); 6. fgets(linea,64,PROD); 7. while ( !feof(PROD)) 8. { 9. printF("Precio de %s? ",linea); 10. scanf (%d,&precio); 11. fprintf(PRODPRE,%s %d\n,prod,precio); 1. fgets(linea,64,PROD); 1. } 2. fclose(PROD); 3. fclose(PRODPRE); Campos de datos Cmo haras un programa que determine a partir del archivo "productos-precios.txt'' el producto ms barato? En este caso se presenta un problema que consiste en cmo separar el contenido de una lnea en nombre del producto y precio.

Si se lee la primera lnea mediante fgets se tiene el problema que se lee la lnea completa y se guarda en una sola variable char[], que habra que "quebrar" de algn modo para trabajar con sus componentes.

El siguiente codigo es errneo: 1. 2. 3. 4. 5. 6. 1.FILE *PROD, *PRODPRE; char linea[64]; int precio; PROD = fopen(producto.txt,r); fgets(linea,64,PROD); fscanf(PROD,%d,&precio);

La operacin fgets(linea,64,PROD) leer toda la lnea ("jabon Lux 344"). Cuando se lea el precio con lect.readInt() tratar de leer "pasta" como un nmero lo que constituye un error en tiempo de ejecucin. La solucin consiste en escribir el archivo delimitando los datos por un caracter especial como dos puntos (:). Para ello es necesario cambiar la lnea 11 a 11. fprintf(PRODPRE,%s:%d\n,prod,precio); De esta forma, el archivo en disco tendr la siguiente estructura: jabn Lux:344 pasta de dientes Odontine:415 champ Sedal:507 ...

En este tipo de archivos los datos que quedan delimitados por ":" se denominan campos (fields). Esta estrategia es muy til para poder incluir varios datos en una misma lnea y despus poder separarlos cmodamente ubicando la posicin de los ":". Por ejemplo:

1. char linea[64], prod[64], *p; 2. int precio; 3. fgets(linea,64,PROD); 4. while ( !feof(PROD) ) 5. { 6. int pos=0; 7. char *p=linea; 8. while (*p++ != ':') pos++; 9. strncpy (prod, linea, pos); 10. precio = atoi (p); /* CONVIERTE DE STRING A ENTERO */ 11. ... 12. fgets(linea,64,PROD); 13. } 14. ... La funcin char *strchr(char *s, char c)

La complicada lnea escrita en rojo ya tiene su funcin en la librera string.h. la funcin strchr recibe un string s y un carcter c y retorna un puntero a la primera aparicin de c en s, o NULL si no la hay. Adems, la funcin atoi convierte el string que recibe como parmetro en un entero, as como ella est tambin atof. Pon tus conocimientos a prueba Modifica el programa de modo que se indique:

Los nombres de los productos ms baratos. Los nombres de los productos ms caros. El precio del cepillo Cuelgate (o Colgate si ud. es argentino J ). Nmero de productos.

Problema resuelto En el archivo "nombres.txt" se encuentran todos los nombres de los alumnos del curso, de la forma un nombre por lnea. Suponiendo que el archivo se encuentra en orden alfabetico, escribe un programa que diga cual es el nombre que ms se repite en el curso y cuantas veces lo hace. Esta solucin es bastante complicada... cmo se puede hacer mas simple? 1. int masRepetida=0, repeticiones=0; 2. FILE *F=fopen (nombres.txt,r); 3. char nombre[64],nombreMasrepetido[64], exNombre[64]; 4. fgets(nombre, 64, F); 5. strcpy(exNombre,nombre); 6. while (!feof(F)) 7. { 8. if (strcmp(nombre,exnombre)==0) 9. repeticiones++; 10. if (repeticiones > masRepetida) 11. { 12. masRepetida=repeticiones; 13. nombreMasRepetido=nombre; 14. } 15. else 16. repeticiones=1; 17. strcpy(exNombre, nombre); 18. fgets(nombre, 64, F); 19. } 20. printf ("El nombre mas comun es %s",nombreMasRepetido); 21. printf (" y aparece %d veces,masRepetida);

Recursividad
Para entender la recursividad, primero hay que entender la recursividad Motivacin La recursividad es una herramienta poderosa que permite solucionar problemas bastante complejos de forma simple y eficiente. Una definicin bastante simplista es que un mtodo recursivo es aquel mtodo que se invoca a s mismo. Considera el siguiente ejemplo: 1. 2. 3. 4. 5. 6. 7. int NumeroDigitos(int n) { if (n <= 9) return 1; else return 1+NumeroDigitos(n/10); }

Ac el mtodo NumeroDigitos se invoca a s mismo en la lnea 6. El programa iterativo equivalente es 1. int NumeroDigitos (int n) 2. { 3. int cifras=1; 4. while (n>9) 5. { 6. cifras=cifras+1; 7. n=n/10; 8. } 9. return cifras; 10. }

Qu hace el computador con el siguiente llamado? int x=NmeroDigitos(832); 1) Llamar al mtodo NmeroDigitos con el argumento 832. Como n es mayor que 10... 2) ...el mtodo se invoca a s mismo con el argumento 83 (n/10) 3) Como n(83) es mayor que 10, el mtodo vuelve a llamarse con el valor 8 (n/10) 4) n es menor que diez, por lo que el mtodo que fue llamado con el valor 8 devuelve 1 5) El mtodo que fue llamado con 83 devuelve 1+1, es decir, 2 6) El mtodo devuelve 2+1=3 7) en x se almacena 3 El siguiente diagrama muestra lo anterior:

Seguir la ejecucin suele ser bastante dificil en casos ms complejos. Una forma ms simple de abordar el funcionamiento de este mtodo es definir el problema de encontrar la cantidad de dgitos de un nmero en funcin de s mismo y a partir de un caso bsico: 1. La cantidad de cifras de un nmero menor que 10 es 1 (caso bsico) (lneas 2-3) 2. La cantidad de cifras de un nmero con ms de 1 cifra es la cantidad de cifras del nmero dividido en 10 ms uno (lnea 5) Si te fijas, en la segunda parte de la definicin, aparece destacado con negritas la definicin recursiva, esa definicin se conoce como invariante. Una vez que uno ha encontrado una definicin recursiva para un problema, suele ser fcil programarla. Considera el problema de escribir en base binaria un nmero n. Los nmeros en base 2 usan slo dos dgitos, 0 y 1. Tienen algunas propiedades interesantes. Una de ellas es que la divisin entera de un nmero binario por dos se puede llevar a cabo borrando la ltima cifra (anlogamente a la divisin por 10 de un nmero en base 10): 17/2=10001/2=8=1000 31/2=11111/2=15=1111 37/2=100101/2=18=10010 Lo que lleva a que la notacin binaria de un nmero n es la notacin binaria de n/2 seguida de un 1 si n es impar o 0 si n es par (*). Por ejemplo, 27 es 11011, que es la notacin de 13 (1101) seguido de 1, pues 27 es impar. 13 es 1101, que es la notacin de 6 (110) seguido de 1, pues 13 es impar 6 es 110, que es 3 (11) seguido de 0, pues 6 es par 3 es 11, que es 1 seguido de 1, pues 3 es impar La definicin anterior (*) es recursiva!:La notacin binaria de un nmero n es la notacin binaria de n/2 seguida de un 1 si n es impar o 0 si n es par (*).

El caso bsico es cuando n es 1 o 0. En ese caso, la notacin es la misma en binario o decimal. El programa recursivo queda: 1. void EscribirEnBinario(int n) 2. { 3. if (n<=1) 4. printf (%d,n); 5. else 6. { 7. EscribirEnBinario (n/2); 8. print (%d, n % 2); 9. } 10. } La potencia de un nmero real tambin puede definirse recursivamente: xn es 1 si n = 0 xxn-1 si n>0 1/(xn) si n<0 La funcin es trivial:
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. float potencia (double x, int n) { if (n==0) { return 1; } else { if (n<0) { return 1/potencia(x,abs(n)); } else { return x*potencia(x,n-1); }; }; }

Nota que si la cantidad de veces que se llama el mtodo a s mismo es (n-1). Existe otra definicin recursiva que nos permitir efectuar slo log2(n) llamados: xn es 1 si n = 0 1/(xn) si n<0 xn/2xn/2 si n es par (/ es divisin entera) xx(n-1)/2 x(n-1)/2 si n es impar

1. float potencia (double x, int n) 2. { 3. if (n==0) 4. { 5. return 1; 6. } 7. else 8. { 9. if (n<0) 10. { 11. return 1/potencia(x,abs(n)); 12. } 13. else 14. { 15. if (n%2==1) /* IMPAR */ 16. { 17. double p=potencia(x, (n-1) div 2); 18. return x*p*p; 19. } 20. else /* PAR */ 21. { 22. double p=potencia(x, n div 2); 23. return p*p; 24. } 25. } 26. } 27. }

La recursividad tambin se puede aplicar sobre arreglos. Por ejemplo, si quieres calcular la suma de todos los elementos de un arreglo puedes usar la siguiente definicin recursiva: Si A no tiene elementos, su suma es 0 Suma Ai(i=1..n) = Suma Ai (i=1..n-1) + An

1. 2. 3. 4. 5. 6. 7.

int suma (int A[], int n) { if (n==0) return 0; else return a[n-1]+suma(a,n-1); }

Y el menor valor de un arreglo? Es fcil darse una definicin recursiva:


el menor elemento de un arreglo que tiene slo 1 elemento es el elemento el menor elemento de un arreglo de n elementos es el menor entre el menor elemento del arreglo considerando solo los primeros (n-1) elementos y el elemento n int menor (int A[], int n) { if (n==1) return=A[0]; else return min(A[n-1],menor (A, n-1)); }

1. 2. 3. 4. 5. 6. 7.

Problemas Resueltos Pregunta 1 La Sucesin de Fibonacci es una secuencia de nmeros naturales, que empieza con 0 y 1, y cuyos siguientes trminos estn definidos como la suma de los dos anteriores: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... Luego podemos definir la funcin de Fibonacci de manera recursiva: Fibonacci(n):

0, si n=0 1, si n=1 Fibonacci(n-2)+Fibonacci(n-1), si n>1

A partir de esta definicin se pide una funcin recursiva que genere el n-esimo trmino de la sucesin. El nmero n se debe pedir al usuario va teclado. (Tpico ejemplo de recursividad con dos casos base). Solucin int fib(int n) { if (n ==0) { return 0; } else if (n==1) { return 1; } else { return (fib(n-1)+fib(n-2)); } }

Pregunta 2 Escriba un programa que sirva para saber las posibilidades de movimiento de un caballo en una partida de ajedrz de una casilla a otra (por ejemplo para comer una pieza). Para ello se ha creado una matriz A[8][8] la cual contiene un mapa del tablero (0 si la casilla est vaca o 1 si est ocupada) y se debe crear un programa donde ingresando el punto de inicio y el de destino diga si existe una combinacin de movimientos posibles que lo lleven del inicio al destino sin que pase a travs de una casilla ocupada o no. (Problema difcil!!!!, estudienlo) Solucin /* 1 es SI HAY, 0 es NO HAY */ int recorrer(int A[][], int i, int j, int p, int q) { if (i<0 || i>=8 || j<0 || j>=8) return false; /* Fuera del tablero */ if ((i==p) && (j==q)) return true; if (A[i][j]==1) return false; A[i][j]=1; /* Mismo lugar */ /* casilla ocupada */ /* Ocupo la casilla */ /* As evito ciclos */ return recorrer (A, i-2, j-1, p,q) || recorrer (A, i-2, j+1, p,q) || recorrer (A, i-1, j-2, p,q) || recorrer (A, i-1, j+2, p,q) || recorrer (A, i+1, j-2, p,q) || recorrer (A, i+1, j+2, p,q) || recorrer (A, i+2, j-1, p,q) || recorrer (A, i+2, j+1, p,q); }

Pregunta 3 Un copo de nieve se puede definir como un punto, varias ramas en crculo que salen de l y luego un copo de nieve de 1/3 del tamao anterior en la punta de cada una de esas ramas. Las ramas son del mismo tamao y estn dispuestas con el mismo ngulo entre ellas. Se pide escribir una funcin recursiva: dibuja_arbol(int x, int y, int n, int l, int nivel) Donde (x,y) son las coordenadas del centro; n es el nmero de ramas; l es el largo de las ramas y nivel es el nmero de niveles que quiero (en el ltimo nivel se dibuja un crculo de radio l). Por ejemplo, del llamado dibuja_arbol(400,300,6,200,4) se obtiene: Para calcular las ramas, deben encontrar el ngulo alfa y las coordenadas de las puntas de todas las ramas, usando seno y coseno del ngulo correspondiente. Para el caso de 8 ramas se tiene: Para dibujar posee las funciones linea (int x1, int y1, int x2, int y2) que dibuja una lnea entre los puntos (x1,y1) y (x2,y2). Y el mtodo circ (int x, int y, int r) que dibuja una circunferencia de centro (x,y) y radio r.

Solucin #include <math.h> void dibuja_arbol(int x, int y, int n, int l, int nivel) { float alfa = 2*PI/n; if(nivel == 0) { circ(x,y,l); return; } /* Aca se itera con el angulo alfa */ for(int i = 0; i < n; i++) { linea(x, y, (int)(x+l*cos(i*alfa)), (int)(y-l*sin(i*alfa))); dibuja_arbol((int)(x+l*cos(i*alfa)),(int)(y-l*Math.(i*alfa)), n, l/3, nivel-1); } }

Problemas propuestos Problema 1: Las torres de Hanoi Se tienen las torres de Hanoi, en que hay que llevar las n fichas de A hasta C, siempre dejando una ficha mas pequea sobre otra mas grande. Se puede utilizar B para pasar algunas fichas. Plantee el algoritmo recursivo que lo realiza.

Problema 2: Tratamiento de enteros Escriba una funcin recursiva que reciba un entero y retorne un String con los digitos invertidos y separados por *. Por ejemplo: Ingrese un nmero: 12345 5*4*3*2*1

You might also like