miércoles, 26 de marzo de 2008

Capítulo 1

.
Introducción

El lenguaje de programación C es muy similar al lenguaje Pascal pero en algunos aspectos es un poco (o bastante) más complejo.

La primer diferencia que nota el programador cuando pasa de Pascal a C es que el lenguaje C es "case sensitive". Esto significa que es "sensible" a las mayúsculas y minúsculas por lo tanto no es lo mismo definir la variable a que definir la variable A. Serán dos variables distintas.

Las sentencias con las que se codifican las estructuras de control también son "sensibles" a estos cambios así que el if, por ejemplo, se escribe con minúscula y si lo escribimos con algún caracter en mayúscula no se reconocerá como la estructura condicional y no compilará.

Todas las estructruras de control: if, while, for (y otras que no vimos aún como el do-while y el switch) se escriben en minúscula y van obligatoriamente acompañadas por paréntesis.

Comenzaremos analizando el primer programa C.

HolaMundo.c
   1:
2:#include <stdio.h>
3:
4:// el programa principal es la funcion main
5:int main()
6:{
7: // imprime en la standar output (pantalla)
8: printf("Hola Mundo !!!");
9:}
10:

En C el "programa principal" siempre es la función main. Así como en Pascal los bloques de código se delimitan con las palabras begin-end, en C los bloques se delimitan con llaves "{" y "}".

En ANSI C (el lenguaje C puro) los comentarios deben estar encerrados entre "barra asterisco" y "asterisco barra" como se muestra a continuación:

/*
Esto es un comentario

escrito en mas de
una linea de codigo...
*/

Sin embargo en versiones más modernas del compilador y en C++ se admiten comentarios de una sola línea anteponiendo una doble barra (//) como vemos en las líneas 4 y 7 de HolaMundo.c.


Para escribir en "la pantalla" (standard output) utilizamos la función printf. Esta función recibe (en este caso) el string que queremos escribir.

La función printf está definida en una librería llamada stdio.h por este motivo incluimos su definición (y las de todas las otras funciones definidas en esa librería) con la sentencia
#include <stdio.h>


Problema 1.0
Permitir ingresar el nombre de una persona y luego mostrar la leyenda: "Hola Mundo nombreIngresado".

Aquí es donde comienzan a aparecer diferencias más complejas entre Pascal y C. En C no existe el tipo de datos string. Los string se implementan sobre arrays de caracteres. Veamos el ejemplo:
   1:
2:#include <stdio.h>
3:
4:int main()
5:{
6: // defino un array de 30 caracteres
7: char nombre[30];
8:
9: printf("Ingrese su nombre: ");
10:
11: // lee por teclado (standard input) y
12: // almacena el texto leido en la variable nombre
13: scanf("%s",nombre);
14:
15: // imprimo "Hola Mundo" y luego el valor nombre
16: printf("Hola Mundo %s\n ",nombre);
17:}
18:

Para leer por teclado utilizamos la función scanf. Esta función recibe una "máscara" que indica el tipo de dato que se va a leer y, una vez leido, lo almacena en la variable nombre.

La máscara que identifica el tipo de dato string es "%s". Luego veremos que para identificar un número entero la máscara que se utiliza es "%d", y (en general) para cada tipo hay una máscara definida.

Luego de leer el nombre utilizamos la función printf para mostrar la salida. Como la salida está compuesta por un texto literal ("Hola Mundo") más un string variable (la variable nombre) tenemos que indicar como será el resultado final agregando "%s" donde queremos que se intercale el contenido de la variable.


El siguiente ejemplo muestra como intercalando la máscara podemos hacer variar el resultado final.

test.c
   1:
2:#include<stdio.h>
3:
4:int main()
5:{
6: // dimensiona automaticamente el array x
7: char x[]="[HOLA]";
8:
9: // el valor de x sale al final
10: printf("1 - A ver donde sale %s\n",x);
11:
12: // el valor de x sale luego de "donde"
13: printf("2 - A ver donde %s sale\n",x);
14:
15: // el valor de x sale en la siguiente linea
16: printf("3 - A ver donde sale\n%s",x);
17:
18: // escribe "A ver donde" en una linea y
19: // "sale" seguido del valor de x en la linea
20: // siguiente
21: printf("4 - A ver donde\nsale %s\n"
,x);
22:}
23:

La salida será:



En este ejemplo utilizamos un caracter especial: el "barra ene". En C hay caracteres especiales que se utilizan para representar caracteres no imprimibles. Algunos de ellos son:
  • '\n' - (barra ene) - salto de línea
  • '\t' - (barra te) - tabulador
  • '\"' - (barra comilla) - comilla doble
  • '\\' - (barra barra) - contra barra
Por último, notemos también que los strings (cadenas de caracteres) se representan encerrados entre "comillas dobles" (ver línea 7 de test.c) en cambio los caracteres (incluso los caracteres especiales) se representan encerrados entre 'comillas simples'.

El caracter '\' (barra) no puede utilizarse por si mismo ya que C lo considera un "caracter de escape". Si dentro de un string usamos una barra será porque a continuación viene algún caracter especial.

Veamos otro ejemplo sobre el uso de la función printf.

test2.c
   1:
2:#include<stdio.h>
3:
4:int main()
5:{
6: // podemos definir variables e inicializarlas
7: // en la misma linea de codigo
8: char cadena[]="Mi Cadena";
9: int entero=10;
10: double flotante=3.14;
11:
12: printf("cadena=[%s], entero=[%d], flotante=[%f]\n"
13: , cadena
14: , entero
15: , flotante);
16:}
17:

Este ejemplo muestra como intercalar texto literal con variables de diferentes tipos de datos. Utilizamos una variable tipo "string" (recordemos que no existen los strings, se manejan como arrays de caracteres), una variable entera (int) y una flotante (double) y para mostrar sus valores con la función printf utilizamos las máscaras correspondientes según el tipo de dato de cada variable.


Tipos de Datos

Los tipos de datos disponibles en C son los siguientes
  • char - 1 byte con signo
  • short - entero corto
  • int - entero
  • longint - entero largo
  • float - flotante con presición simple
  • double - flotante con doble presición
La longitud de estos tipos de datos dependerá del sistema operativo en el que estemos compilando nuestra aplicación, pero la podemos averiguar facilmente utilizando la función sizeof como muestra el siguiente ejemplo.

muestraSizes.c
   1:
2:#include<stdio.h>
3:int main()
4:{
5: printf("char = [%d]\n",sizeof(char));
6: printf("short = [%d]\n",sizeof(short));
7: printf("int = [%d]\n",sizeof(int));
8: printf("long = [%d]\n",sizeof(long));
9: printf("float = [%d]\n",sizeof(float));
10: printf("double = [%d]\n",sizeof(double));
11:}
12:

En Windows 2000 (plataforma Win32) este programa arroja los siguientes resultados:



Además, C permite aplicar el modificador unsigned a los tipos enteros (char, short, int, long) por lo quel cualquiera de estos tipos de dato puede duplicar su capacidad para valores positivos eliminando el bit de signo.

char c; //admite valores entre -128 y 127
unsigned char c; // admite valores entre 0 y 255

Recordemos que en C no existe el tipo string. Las cadenas de caracteres deben implementarse como arrays de caracteres (esto lo veremos en detalle en el capítulo 2).

Por último, en C tampoco existe el tipo boolean. Los valores lógicos (o booleanos) se implementan con variables enteras y se considera que el valor 0 es falso y cualquier otro valor distinto de cero es verdadero.

En el siguiente ejemplo utilizamos una variable entera fin y la manejamos como si fuera una variable booleana.

testBoolean.c
   1:
2:#include<stdio.h>
3:int main()
4:{
5: int fin=0, i=0;
6:
7: // itero mientras fin sea falso (while NOT fin)
8: while( !fin )
9: {
10: i=i+1;
11: printf("%d\n",i);
12:
13: if( i>=10 )
14: {
15: fin=1;
16: }
17: }
18:}
19:

Otra opción sería definir el tipo BOOLEAN y las constantes TRUE y FALSE como muestra el siguiente ejemplo.

testBoolean2.c
   1:
2:#include<stdio.h>
3:
4:#define BOOLEAN int
5:#define FALSE 0
6:#define TRUE 1
7:
8:int main()
9:{

10:
int i=0;
11: BOOLEAN fin=FALSE;
12:
13: // itero mientras fin sea falso (mientras NOT fin)
14: while( !fin )
15: {
16: i=i+1;
17: printf("%d\n",i);
18:
19: if( i>=10 )
20: {
21: fin=TRUE;
22: }
23: }
24:}
25:

Las definiciones #define (y todas las instrucciones que comienzan con " # ") son directivas de preprocesador, tema que veremos más adelante.


Problema 1.1
Leer un valor, indicar si el valor ingresado es mayor que 10.
   1:
2:#include<stdio.h>
3:int main()
4:{
5: int v;
6: printf("Ingrese un valor: ");
7:
8: // como no existen lo parametros por referencia
9: // tenemos que pasarle a scanf la direccion
10: // de memoria de la variable v (su referencia)
11: scanf("%d",&v); // &v es la direccion de v
12:
13: if( v>10 )
14: {
15: printf("El valor es mayor que 10\n");
16: }
17: else
18: {
19: printf("El valor NO es mayor que 10\n");
20: }
21:}
22:

A simple vista el código es similar a Pascal pero hay un detalle muy importante: &v (ver línea 11).

En C no existen los parámetros por referencia. Todos los parámetros son pasados por valor por lo tanto para que una función pueda modificar el contenido de alguno de los parámetros que recibe debemos pasarle su dirección de memoria. O sea: su referencia.

Si definimos la variable v entonces &v (léase "ampersand v") es la referencia al contenido de la variable v.

En este punto el lector se estará preguntando -Como...? En el problema 1.0 que se analizó más arriba utilizamos la función scanf para leer un string en una variable de tipo char[] (array de caracteres). La función scanf pudo asignar sin problemas el valor leido a la variable nombre y no fue necesario pasarle &nombre...? Por que ahora tenemos que pasarle la dirección de la variable v y en el ejemplo anterior no se necesitó pasar la dirección de la variable nombre?-

La respuesta a esta pregunta es la siguiente: Un array es en si mismo una referencia al primero de sus elementos y todos sus elementos tienen direcciones de memoria consecutivas. Por lo tanto en el problema 1.0 la variable nombre representa la dirección del primer caracter del array. Entonces &nombre representará la dirección de la dirección del primer caracter del array.


Problema 1.2
Idem anterior pero indicar si el valor ingresado es mayor, menor o igual a 10.
   1:
2:#include<stdio.h>
3:int main()
4:{
5: int v;
6: printf("Ingrese un valor: ");
7: scanf("%d",&v); // &v es la direccion de v
8:
9: if( v>10 )
10: {
11: printf("El valor es mayor que 10\n");
12: }
13: else
14: {
15: // el operador de comparacion es " == "
16: if( v == 10 )
17: {
18: printf("El valor es igual que 10\n");
19: }
20: else
21: {
22: printf("El valor es menor que 10\n");
23: }
24: }
25:}
26:

En la línea 16 utilizamos el operador " == " para comparar el valor de la variable v. En lenguaje C el operador " == " es el operador de comparación mientras que el operador " = " es el operador de asignación.


Problema1.3
Leer tres valores numéricos. Indicar cual es mayor, medio y menor.
Nota: Se asume que los tres valores serán diferentes.
   1:
2:#include<stdio.h>
3:int main()
4:{
5: int a,b,c;
6: printf("Ingrese tres valores: ");
7: scanf("%d %d %d",&a,&b,&c);
8: if( a>b )
9: {
10: if( a>c )
11: {
12: printf("Mayor: %d\n",a);
13: if( b>c )
14: {
15: printf("Medio: %d\n",b);
16: printf("Menor: %d\n",c);
17: }
18: else
19: {
20: printf("Medio: %d\n",c);
21: printf("Menor: %d\n",b);
22: }
23: }
24: else
25: {
26: printf("Mayor: %d\n",c);
27: printf("Medio: %d\n",a);
28: printf("Menor: %d\n",b);
29: }
30: }
31: else
32: {
33: if( b>c )
34: {
35: printf("Mayor: %d\n",b);
36: if( a>c )
37: {
38: printf("Medio: %d\n",a);
39: printf("Menor: %d\n",c);
40: }
41: else
42: {
43: printf("Medio: %d\n",c);
44: printf("Menor: %d\n",a);
45: }
46: }
47: else
48: {
49: printf("Mayor: %d\n",c);
50: printf("Medio: %d\n",b);
51: printf("Menor: %d\n",a);
52: }
53: }
54:}
55:


Operadores Lógicos

La siguiente tabla permite comparar los operadores lógicos usados en Pascal y C.



Con esto podemos codificar una solución alternativa al problema 1.3.
   1:
2:#include<stdio.h>
3:int main()
4:{
5: int a,b,c;
6: int mayor,medio,menor;
7:
8: printf("Ingrese tres valores: ");
9: scanf("%d %d %d",&a,&b,&c);
10:
11: if( a>b && a>c )
12: {
13: mayor=a;
14: if( b>c )
15: {
16: medio=b;
17: menor=c;
18: }
19: else
20: {
21: medio=c;
22: menor=b;
23: }
24: }
25: else
26: {
27: if( b>a && b>c )
28: {
29: mayor=b;
30: if( a>c )
31: {
32: medio=a;
33: menor=c;
34: }
35: else
36: {
37: medio=c;
38: menor=a;
39: }
40: }
41: else
42: {
43: mayor=c;
44: if( a>b )
45: {
46: medio=a;
47: menor=b;
48: }
49: else
50: {
51: medio=b;
52: menor=a;
53: }
54: }
55: }
56:
57: printf("Mayor: %d\n",mayor);
58: printf("Medio: %d\n",medio);
59: printf("Menor: %d\n",menor);
60:}
61:


Problema 1.4

Imprimir por pantalla los primeros n números positivos (el valor n se ingresa por teclado).
   1:
2:#include<stdio.h>
3:int main()
4:{
5: int i,n;
6:
7: printf("Ingrese un valor: ");
8: scanf("%d" ,&n);
9:
10: i=1;
11: while( i<=n )
12: {
13: printf("%d\n",i);
14: i=i+1;
15: }
16:}
17:

La codificación C de este problema es casi idéntica a la codificación Pascal. Veamos ahora una codificación alternativa con la que podemos lograr un código más compacto.
   1:
2:#include<stdio.h>
3:int main()
4:{
5: // inicializamos el valor de i en 1
6: int i=1,n;
7:
8: printf("Ingrese un valor: ");
9: scanf("%d" ,&n);
10:
11: while( i<=n )
12: {
13: // muestra el valor de i, luego incrementa
14: printf("%d\n",i++);
15: }
16:}
17:

En este ejemplo utilizamos el operador " ++ " que permite incrementar el valor de una variable. Notemos que aplicamos el operador ++ a la variable i en la línea 14. C dió prioridad al actual valor de i y luego lo incrementó.

En el capítulo 2 veremos más sobre este tipo de operadores.


Ciclos do-while y for

En C no existe el ciclo repeat-until que estudiamos en Pascal. La sentencia aquí es: do-while y funciona justamente al revés. El primero "itera hasta que se cumpla su condición", el segundo "itera mientras se cumpla su condición".

Utilizando esta estructura el problema 1.4 se puede codificar de la siguiente manera:
   1:
2:#include<stdio.h>
3:int main()
4:{
5: int i=1,n;
6:
7: printf("Ingrese un valor: ");
8: scanf("%d",&n);
9:
10: do
11: {
12: printf("%d\n",i++);
13: }while(i<=n);
14:}
15:

El ciclo for en C es diferente al ciclo for de Pascal. Veamos el problema 1.4 implementado con un ciclo for y luego lo analizaremos.
   1:
2:#include<stdio.h>
3:int main()
4:{
5: // inicializamos el valor de i en 1
6: int i=1,n;
7:
8: printf("Ingrese un valor: ");
9: scanf("%d" ,&n);
10:
11: for( i=1; i<=n; i++ )
12: {
13: printf("%d\n",i);
14: }
15:}
16:

El for se compone de 3 secciones separadas por ";" (punto y coma). La primer sección es la de inicialización de la variable de control. La segunda sección define una condición "while" por lo tanto el for itera mientras se cumpla esta condición. La última sección define el incremento de la variable de control.


Problema 1.5
Leer un conjunto de valores que corresponden a las edades de un grupo de personas.

Indicar:
a - Cuantas personas (sus edades) se ingresaron.
b - Cuantas son mayores de edad (21 años o más).
b - Edad promedio de las personas.

El conjunto de valores finaliza cuando se ingresa una edad menor que cero.
   1:
2:#include<stdio.h>
3:int main()
4:{
5: int e, mayores=0, cantPersonas=0, sumEdades=0;
6: double edadProm;
7:
8: printf("Ingrese Edad: ");
9: scanf("%d",&e);
10:
11: while( e>=0 )
12: {
13: // incremento la cantidad de personas
14: cantPersonas++;
15:
16: // acumula las edades
17: sumEdades+=e;
18:
19: if( e>=21 )
20: {
21: // contador de mayores de 21
22: mayores++;
23: }
24:
25: printf("Ingrese Edad: ");
26: scanf("%d",&e);
27: }
28:
29: // calculo el promedio
30: edadProm=sumEdades/cantPersonas;
31: printf("Cantidad de Persona: %d\n",cantPersonas);
32: printf("Mayores de 21: %d\n",mayores);
33: printf("Edad Promedio: %f\n",edadProm);
34:}
35:













.

3 comentarios:

Unknown dijo...

pablo puede ser que con el editpadpro, al final de cada codigo en c, tenga que agregar getch(); (que significa?) para que me compile bien y no me tire el siguiente error:

warning: no newline at end of file

saludos, guido

Unknown dijo...

Buenas! estoy teniendo el mismo problema que Guido acerca del mensaje de "warning: no newline at end of file" al intentar compilar, no estoy enterado de si tengo que agregar o no getch(); al final. Intente agregarlo pero no me soluciono el problema.
Saludos

Mati Silversteyn dijo...

Buenas, tengo una pregunta: Si bien no existen los strings (son char), cuando yo leo de un archivo de texto con el fgets, lo que hago es leer toda la linea y almacenarlo en un vector de chars. Esa linea la conviertyo a string porque utilizo funciones de manejo de strings con esa linea, pero luego como la convierto a char para volver a guardarla en otro archivo de texto??
Entonces, lo que preciso son 2 cosas:
- Como convierto esa linea a string para aplicarle las funciones de manejo de string
- Como convierto nuevamente a char para guardarla nuevamente en un archivo de texto?
Muchas gracias!