Repaso pruebas de escritorio y simplificación de programas

1. Hacer la prueba de escritorio del programa siguiente:

int a = 100 ;
a = a * 2 ;
a = 6 ;

¿Al final, importaba el valor inicial de la variable a?

2. Hacer la prueba de escritorio del programa siguiente:

int a = ? ; /* algun valor */
int b = ? ; /* algun valor */

b = 2;
b = a + b;
a = a - 1;
a = 4;

¿Pueden reducir la cantidad de sentencias?

3. Sin hacer la prueba de escritorio, observar el programa siguiente:

/* 1.3 */
int a = 2;
int i;
for(i = 0; i < 5; i = i + 1){
   a = 0;
   a = a + 1;
   a = i + 1;
   printf("%d\n", a);
}

¿Se puede obtener el mismo comportamiento con menos sentencias?

scanf

La función scanf es parecida a printf pero hace la operación contraria, que es, leer valores que el usuario ingrese con el teclado. Estos valores luego pueden ser usados como valores en el programa.

Un ejemplo de uso de scanf:

#include <stdio.h>
int main(void) {
    int a;
    printf ("Diga un valor para a:\n");

    scanf("%d",&a);   /* el usuario ingresa un valor entero,
                         que se asigna a la variable a */

    printf ("el valor es: %d\n",a);
    return 0;
}

No terminar un formato con \n en scanf porque el salto de línea ya sirve para confirmar la entrada.

Usar la documentación de la materia para saber más acerca de scanf.

Recordatorio: qué son las variables

Una variable es constituida de:

Pasar variables por referencia y la función scanf

Cuando llamamos una funcion siempre se da a la función llamada algun valor:

hola(10);            /* valor constante 10 */
hola(x);             /* valor de la variable x */
hola(cuadrado(x));   /* valor de cuadrado(x) */

Es decir, se evalua el valor antes de que se haga la llamada.

Si en lugar del valor de una variable, damos la dirección en la memoria de la variable, entonces la función que llamamos es capaz de modificar el valor de esa variable.

En el lenguaje C, la expresión "&x" se puede leer como "la dirección de x".

Cuando llamamos una funcion pasandole un nombre de variable x se dice que la pasamos por valor:

funcion1(x);

Cuando lo hacemos pasandole la direccion de x, se dice que la pasamos por referencia:

funcion2(&x);

scanf y cálculo de la edad

Hacemos un programa que pide el año de nacimiento y el año actual al usuario, y muestre la edad calculada del usuario. El programa se puede equivocar de un año (para la gente que todavía no cumplió años).

Definir un programa edad.c que tenga:

assert

Ejemplo de uso de la función assert:

#include <stdio.h>
#include <assert.h>
int main(void) {
    int a;
    printf ("Diga un valor POSITIVO para a:\n");

    scanf("%d",&a);

    assert(a >= 0); /* si la condición NO se cumple,
                       el programa se termina acá */

    printf ("el valor es: %d\n",a);
    return 0;
}

La función assert sirve para poner una condición que tiene que cumplirse. Al no cumplirse, la ejecución del programa se termina.

assert es particularmente útil al principio de un algoritmo, cuando solo tiene sentido ejecutarse cuando alguna condición se cumple.

Por ejemplo, un algoritmo de división puede primero comprobar con assert que el divisor es distinto de cero.

En un programa con valores ingresados por el usuario, la estructura suele ser la siguiente:

  1. declaraciones de las variables
  2. ingreso de los valores por el usuario
  3. el algoritmo mismo (empezando por assert)

Modificar su programa de cálculo del MCD de dos enteros para que los valores sean leidos por la función scanf.

Usar la función assert de la bibliotheca assert.h para asegurarse que el programa se siga ejecutando solamente si los valores cumplen con la condición a >= b.

Funciones

Hasta ahora siempre definimos una sola función en nuestros programas: la función main.

Ahora, además del main vamos a definir más funciones, para poder llamarlas desde el main.

Por el momento, vamos a definir funciones sin parámetros y que no devuelven ningun valor, es decir que su definición siempre va a empezar por:

void mi_funcion(void){
  ...
}

int main(void){
  ...

  mi_funcion();  /* ejecutamos mi_funcion */
  ...
}

Robot limpiador

Definir un programa robot_limpiador.c que muestra exactamente la salida siguiente:

1. limpiar
2. girar a la derecha
3. avanzar
1. limpiar
2. girar a la derecha
3. avanzar
1. limpiar
2. girar a la derecha
3. avanzar
1. limpiar
2. girar a la derecha
3. avanzar
FIN

El programa debe tener las funciones pasos y main, y la función pasos se encarga de mostrar los pasos repetidos en la salida.

En uno de sus programas (robot_limpiador.c, mcd.c ...) agregar una función void imprimir_autor(void) que se imprima:

  1. en una primera linea su nombre completo
  2. en una segunda linea el año actual.

Hacer una llamada a esta función desde el main de su programa, justo antes de que se termine.

Numeros aleatorios

dilbertrandom 

Vamos a conocer unas funciones nuevas para conseguir valores aleatorios.

Compilar y ejecutar el programa siguiente:

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int r;
    int i;
    for(i=0; i<5; i++)
    {
        r = rand();
        printf("%d\n", r);
    }
    return 0;
}

La biblioteca stdlib.h proveé la función rand, cuya llamada devuelve un número número aleatorio. Es como tirar un dado o una moneda, salvo que los valores generados están dentro de un rango muy grande (de 0 a la constante RAND_MAX).

Si reemplazamos la expresión rand() por rand() % 100, restringimos los números aleatorios al rango de 0 a 99:

r = rand() % 100;

Compilar este programa y ejecutarlo varias veces. ¿Qué observamos? ¿Es lo que esperamos?

Explicación: rand() es un generador pseudo-aleatorio. Genera una secuencia que no es realmente aleatoria, porque cada número generado dependen del número anterior generado.

Para que esa secuencia parezca aleatoria, debemos inicializar el primer nùmero de la secuencia con algun valor que no sea igual entre dos ejecuciones de nuestro programa.

¡Usemos la hora!

La biblioteca time.h proveé la función time que devuelve la cantidad de segundos desde el 1970. Luego, llamamos la función srand para inicializar la secuencia aleatorio que genera rand():

#include <stdlib.h>
#include <time.h>

int main(void){
  int hora = time(NULL);  /* el número de segundos desde el año 1970 */
  srand(hora);            /* inicializamos la semilla con la "hora" */
  ...
  ...
  ...
  ...
}

Modificar el programa y observar que los valores generados cambian en cada ejecución.

Bucle do ... while

Ya conocemos el bucle while, ahora veamos do ... while que es muy parecido, salvo que garantiza que se ejecuta por lo menos una vez. (Un bucle while puede no ejecutarse si su condición es falsa.)

Su sintaxis es:

do {
  ... /* cuerpo del bucle */
} while (/* CONDICION */);

Lo podemos traducir como "ejecutar CODIGO una vez y repetirlo mientras CONDICION es verdadero".

Juego "Adiviná el número"

Programemos un juego que consiste en adivinar el número que el programa eligió al azar.

La estructura del programa es la siguiente:

  1. el programa elige al azar un número n entre 0 y 10
  2. el usuario ingresa un número u
  3. si u no es el número exacto, el programa dice si n es más grande o más chiquito que el número ingresado
  4. repetimos desde 2. hasta que u sea igual a n

Usemos un bucle do ... while para que el programa sigua pidiendole al usuario que ingreso del valor por el usuario por lo menos una vez.

El programa tiene que usar los mensajes siguientes (o alguna versión de estos que les parece mejor):

Programar el juego en un archivo adivina.c.

Una vez que su juego funciona, agrégenle los cambios siguientes:

Proyecto para el Parcial 3

Tienen hasta el sábado 28 martes 31 de mayo a las 23hs59 para mandar su proyecto.

Si me mandan su programa antes del martes 31 a las 17hs, les puedo hacer una sola devolución por mail para que lo puedan mejorar.

Consignas:

Ideas de mejoras posibles: