Funciones que toman parámetros

Noción de parametro formal y parametro real.

Ejemplo:

#include <stdio.h>

void hola(void){
  printf("Hola mundo!\n");
}

void mostrar1(int a){
  printf("Tengo %d manzanas.\n", a);
}

void mostrar2(int a, int b){
  printf("Tengo %d manzanas y vos %d.\n", a, b);
}

int main(void) {
  int x = 60;

  hola();

  mostrar1(18);
  mostrar1(20);

  mostrar1(x);

  mostrar2(20,25);
  mostrar2(25,20);

  mostrar2(x,x);
  mostrar2(x,100);

  return 0;
}

En qué año estamos

Escribir un programa tiempo.c que tenga una función void mostrar_anho(int a), para que muestre:

Estamos en el 2000!
Estamos en el 2016!
Estamos en el 2999!

Queremos que 2000, 2016, 2999 sean parametros reales en el main cuando llamamos mostrar_anho.

Funciones que devuelven valores

Ver en el apunte "Aprenda C", el uso de la palabra-clave return.

#include <stdio.h>

int cuadrado(int a){
  return a*a;
}

int pi(void){
  return 3;
}

int main(void){
  int x = 0;
  x = pi();
  x = cuadrado(5);
  printf("x vale %d\n", x);
  return 0;
}

Definir funciones Booleanas

En el lenguaje C se interpetan los valores enteros no nulos como "verdaderos" y nulos como "falsos".

Es decir, la condición siguente siempre es falsa:

if ( 0 ) {
  ...
}

Las condiciones siguientes siempre son verdaderas:

if ( 1 ) {
  ...
}
while ( 2 ) {
  ...
}
for ( ... ; 3 ; ... ){
  ...
}

Pero se suele usar el valor "1" para indicar "verdadero".

Escribir un programa bool.c que tenga el main siguiente:

int main(void){
 int i;
 for ( i = 0; i < 30 ; i = i + 1 ){
   if ( divisible_por_3(i) ){
    printf("%d es divisible por 3.\n", i);
   }
 }
 return 0;
}

Completar este programa con la función int divisible_por_3(int a) que devuelve el valor 1 (uno) si su argumento es divisible por 3 y 0 (cero) sino. Asegurarse que el programa es correcto.

Función que devuelve un entero del usuario

Sobre papel, escribir una función de tipo int leer_entero(void) que lea un entero ingresado por el usuario usando scanf y lo devuelve.

El preprocesador

El rol del preprocesador y la instrucción #include

La compilación de un programa C ocurre en distintas etapas.

Podemos distinguir en este proceso 2 pasos consecutivos:

En nuestro código siempre usamos #include <stdio.h> al principio. La palabra #include es una instrucción para el preprocesador. Se encarga de incluir el archivo stdio.h en nuestro archivo. Es decir, el preprocesador copia dentro de nuestro archivo el contenido del archivo stdio.h. Ese último tiene las declaraciones de funciones de entrada y salida, como printf y scanf.

Si queremos, podemos usar #include en cualquier lado de nuestro código fuente para copiar el contenido de otros archivos.

Definición de macros con #define

Vamos a usar la instrucción #define del preprocesador para definir macros. Una macro es una palabra que va a ser substituida por algun valor fijo que definimos:

#define PI 3.14159

Por ejemplo:

#include <stdio.h>

#define PI 3.14159

int main(void){
  printf("Si un circulo tiene un radio de %d cm,\n", 5);
  printf("entonces su diametro es de %f cm\n", 5 * PI * 2);
  printf("y su area es de %f cm2. \n", 5 * 5 * PI);
  return 0;
}

Típicamente usamos una macro (en lugar de una variable) para valores que tienen que ser constantes e iguales en todo el programa. Permite definir una constante en un solo lugar (y modificarla en un solo lugar si hace falta) y usarla en todos lados.

Cuando usamos #define PI 3.14159, el preprocesador busca todas las ocurrencias de la palabra PI en nuestro programa y las reemplaza por 3.14159. Luego ocurre la verdadera etapa de compilación.

Arreglos

Un arreglo (o vector, o lista) es una variable que almacena varios valores dispuestos succesivamente en la memoria y accesibles por su posición.

Por ejemplo, si declaramos un arreglo de enteros de tamaño 10 con valores iniciales, escribimos:

int arr[10] = {18,17,20,17,23,23,25,22,23,20};

Esta declaración tiene el efecto de definir la variable arr, y un espacio en la memoria donde se guardan los 10 valores del arreglo:

        +----+----+----+----+----+--..--+----+----+
arr:    | 18 | 17 | 20 | 17 | 23 |  ..  | 23 | 20 |
        +----+----+----+----+----+--..--+----+----+

indice:   0    1    2    3    4           8    9

Para referirse a valores individuales de un arreglo, usamos los corchetes. En el caso presente, el valor de arr[0] es 18, el de arr[1] es 17, etc. El último valor, arr[9] es 20.

¡Cuidado con los índices! Empiezan con 0. Por lo cual si un arreglo es de tamaño n, su último elemento es de indice n-1.

En el lenguaje C se puede trabajar con arreglos de tamaño fijo o variable. Sin embargo es más simple trabajar con arreglos de tamaño fijo, porque no necesitamos pedir memoria al sistema operativo para trabajar con esos, se hace automáticamente. En Programación 2 (segundo cuatrimestre) sí vamos a ver arreglos de tamaño variable.

Una manera cómoda de recorrer los valores de un arreglo es usar un bucle for:

int temperaturas[7] = {21,23,22,22,20,19,23};
int i;
for( i = 0; i < 7; i++){
    printf("En el dia %d, hizo %d grados.\n", i + 1, temperatura[i]);
}

Tenemos esta constante 7 que aparece dos veces en nuestro programa. Si seguimos agregando instrucciones a nuestro código, tenemos que acordarnos del valor de esta constante, lo que puede ser propenso a errores. Para evitar eso, definamos una macro:

#define DIAS 7

int temperaturas[DIAS] = {21,23,22,22,20,19,23};
int i;
for( i = 0; i < DIAS; i++){
    printf("En el dia %d, hizo %d grados.\n", i + 1, temperatura[i]);
}

Podemos modificar los elementos de un arreglo:

#define DIAS 7

int temperaturas[DIAS] = {21,23,22,22,20,19,23};
int i;
for( i = 0; i < DIAS; i++){
    printf("En el dia %d, hizo %d grados.\n", i + 1, temperatura[i]);
}
/* oh no! el calentamiento global no para! */
for( i = 0; i < DIAS; i++){
    temperaturas[i] = temperaturas[i] + 2;  /* va a hacer 2 grados mas de calor! */
}

Cálculo del máximo y mínimo

En un archivo temperatura.c, declarar un arreglo de tamaño 7 con valores enteros de su elección.

En un bucle for, calcular el valor máximo de ese arreglo. Mostrarlo.

Calcular el valor mínimo de ese arreglo. Mostrarlo.

Variables de tipo flotante

Hasta ahora siempre hemos trabajado con variables de tipo entero int. Podemos trabajar con variables de tipo "flotante" o "coma flotante" que permiten representar valores decimales.

#include <stdio.h>

int main(void){
 float x = 1.0 / 3;
 /* escribimos 1.0 en lugar de 1 para que la división se
    haga sobre valoes flotantes, y no enteros
 */
 printf("x == %f\n", x);
 return 0;
}

Una variable de tipo float puede tener errores de aproximación. Por ejemplo no se puede representar el verdadero valor de 1/3 ( 0.333333...) porque la representación en la computadora tiene que ser truncada (un float tiene solo un espacio finito para representar valores).

Cálculo de promedios

Modificar el programa temperatura.c para calcular y mostrar la temperatura promedia.

Segmentation fault

Con el lenguaje C, es posible que un programa compile sin errores ni warnings, pero que tenga un error cuando se ejecuta.

Si tratan de acceder a un elemento de un arreglo que está fuera del rango posible es probable que el programa le tire el error Segmentation Fault, en castellano "violación de segmento".

El segmento violado en ese caso es un segmento de memoria. El error significa que intentaron acceder a una parte de la memoria que no está autorizada para su programa.

Pueden agregar el flag -g a tcc para tener más información cuando se encuentran con un Segmentation Fault:

$ tcc -g -run programa.c

scanf y arreglos

Modificar temperatura.c para pedir al principio, que el usuario ingrese los valores del arreglo de temperaturas.

Entrega

Mandar temperatura.c.

Más lectura