Lectura III:
3.1. Introducción. Dado que la mayoría de los lectores de éstas notas tienen a PASCAL como su vernáculo seguiremos la estrategia de aprender C contenstando la pregunta cómo se hace en C tal cosa de PASCAL. Así fué que lo aprendió quien escribe estas notas y atestiguo que no es la mejor manera de hacerlo. Existe el riesgo de que se pierda el espíritu del lenguaje (si es que se puede hablar de tal cosa).
EL lenguaje C no es realmente un lenguaje de alto nivel ni tampoco es de bajo nivel; es una especie de intermedio. Esto lo hace muy versátil aunque en ocasiones dificil de depurar ya que todo se vale. C especialmente poderoso en el manejo de punteros, aunque veremos poco de ésto en esta introducción al lenguaje dado que no escribiremos programas terríblemente complicados en este curso. De todas maneras, los programadores de PASCAL deben tener menos problemas que otros programadores aclimatándose a éste lenguage ya que hay muchas similitudes entre PASCAL y C; mucha de la puntuación, el estilo general de los bloques de instrucciones, la manera en que se manejan y el encapsulamiento de tipos de datos complejos son algunos de los aspectos en que éstos se parecen.
En contraste con C, PASCAL fué diseñado originalmente con la intención de ser una herramienta para la enseñanza de los fundamentos de la programación y su éxito fué tal que aún hoy el 60% de las universidades, incluyendo la nuestra, lo utilizan en sus cursos introductorios de programación. Un requisito para un lenguaje con éstos propósitos es que esconda los aspectos más detallados del hardware y el sistema operativo. Es por tanto más general que C y menos capaz de alcanzar los niveles de rendimiento de C. Aunque PASCAL ha evolucionado, éste es raramente utilizado para desarrollos de proyectos grandes como sistemas operativos, paquetes enlatados, etc.
3.2. Estructura de un programa. Un programa es una sucesión de declaraciones de variables globales y de funciones. En C no hay el equivalente de los procedures de PASCAL pero sí de las funciones. Tampoco existen subprogramas dentro de subprogramas. Todas las funciones son hermanas y no importa el orden en éstas se escriba (aquí miento un poco). El programa principal es la funcion llamada main y siempre tiene que haber una y sólo una de éstas en un programa. El siguiente es un programa que computa factorial de N.
#include <stdio.h>
int factorial(int n)
{
if(n <= 0) return 1;
else return n * factorial(n-1);
}
main()
{
int n;
int factoial(int);
scanf("%d", &n);
printf("Factorial de %d es %d.\n", n, factorial(n));
}
El algoritmo usado aqui debe serle familiar a la lectora o lector. La primera instrucción es realmente para el preprocesador del compilador. El proprocesador va a leer un archivo llamado stdio.h y va a incluirlo en ese lugar antes de compilar el programa. En este ejemplo incluir stdio.h es necesario por que éste contiene los encabezados de las funciones de I/O que se usan en el programa. La próxima línea indica que comienza la declaración de una función llamada factorial que recibe como argumento un entero y devuelve un entero. El resto de esta función debe ser legible. Luego viene la declaración de la función main. Como no se especifica qué valor devolverá el compilador supondrá que devuelve un int. En main se declara una variable local n y se provee el encabezado de la función factorial. Esta última hace las veces de los forward procedure declarations de PASCAL.
3.3. Constantes. Las constantes numéricas se escriben de manera similar que en PASCAL, aunque hay maneras compactas de denotar constantes en octal o hexadecimal. Los caracteres son ´c´ aunque hay maneras compactas de denotar cualquier caracter por su código ASCII. Las constantes de texto se delimitan por comillas dobles.
3.4. Variables. Los nombres de las variables se construyen de manera similar a PASCAL con la excepción de que en C las letras mayúsculas y minúsculas son distintas por lo que los nombres var1 y Var1 denotan variables diferentes. Tenga mucho cuidado con lo anterior. Toda variable tiene que ser declarada usando el formato
<tipo_de_variable> < lista_de_nombres_de_variables >;
La <lista_de_nombres_de_variables> es una lista <nombres_de_variables> separada por comas. Los tipos básicos de variables son int, char, float y double.
3.5. Aritmética. Toda la aritmética que podemos hacer en PASCAL la podemos hacer en C de la misma manera. Pero podemos hacer más en C. Por ejemplo
Similarmente -=, *= y /= restan, multiplican o dividen la variable a la izquierda por la expresión de la derecha. Las siguientes son instrucciones válidas en C:
La diferencia entre las últimas dos es que, suponiendo que el valor de i fuera 2 antes de ejecutarse éstas, en la primera se multiplicaría la z por 2 y en la segunda por 3. En ambas el valor de i terminaría siendo 3.
Una diferencia importante entre PASCAL y C es que en éste último se puede hacer aritmética con todo tipo de variable, no sólo de tipos int y float, sino también caracteres, punteros, es decir, toda variable. El siguiente pedazo de programa es perfectamente legal en C:
char c;
c = 'A';
c++;
printf("%c", c);
y su efecto es que imprime la letra B.
3.6. Funciones. La sintaxis usada para escribir una función es
<nombre_de_función> (<lista_de_declaraciones_de_variables >)
{<declaraciones_de_variables_locales> <instrucciones >}
Las instrucciones se separan con ; al igual que en PASCAL.
3.7. Archivos de encabezado. En ocasiones es necesario incluir al comienzo del programa una serie de instrucciones para el preprocesador, el programa que prepara su códico para el compilador, de modo que éste incluya los encabezados de las funciones de librería que usted va a utilizar. Así, si usted va a hacer I/O es probable que necesite la siguiente instrucción:
#include <stdio.h>
Usualmente la documentación sobre estas funciones dice qué archivos se deben incluir en su programa. Verá un ejemplo más adelante en la discusión sobre I/O.
3.8. Decisiones. El lector observador habrá notado que entre los tipos de variable básicos no se mencionó uno equivalente al BOOLEAN. Es que no lo hay, y tampoco hay tal cosa como una expresión booleana aunque si hay operadores lógicos. Así que decimos que la instrucción IF se escribe así:
if ( expresión ) instrucción ;
ó
if ( expresión ) instrucción; else instrucción ;
Si el resultado de la expresión es algo que no es cero entonces se ejecuta la instrucción que está después del par'entesis, y si no, la que está después del else. Los operadores lógicos son
Las comparaciones se hacen con
==, !=, <, <=, > y >=.
Un error muy común y difícil de encontrar es escribir x = y cuando se quiere escrbir x == y. Es muy difícil de encontrar por que el primero no producirá mensaje de error.
3.9. Ciclos. Los formatos para los ciclos son:
while ( expresión ) instrucción ;
do instrucción while ( expresión ) ;
for ( expresión1 ; expresión2 ; expresión3 ) instrucción ;
La semántica de la instrucción while debe ser evidente a la luz de su experiencia con PASCAL y la discusión sobre if de arriba. El ciclo do - while es similar al until de PASCAL. El ciclo for es algo distinto y le garantizo que una vez le agarre el gusto no podrá vivir sin él. Dicho de manera resumida, la expresión1 se ejecuta al comienzo de la ejecución del ciclo sólamente, la expresión2 se usa para determinar cuando se detiene el ciclo (similar a while) y la expresión3 se hace cada vez que se ejecuta el ciclo. Así que
for i = 1 to 10 do x := x + 3.0;
se traduce a
for ( i = 1; i <= 10; i++) x += 3.0 ;
Aunque no entremos en detalle aquí recuerde que expresión1, expresión2 y expresión3 no tienen que ser una asignación inicial, una expresión lógica o un incremento de variable respectivamente. De hecho, pueden estar vacías. La ejecución de la instrucción break dentro de un ciclo produce que la ejecución del ciclo termine y se continúe con lo que está después del éste. La ejecución de continue hace que el ciclo pase a la próxima iteración.
3.10. Insumo/Producto. Las instrucciones para leer del teclado o imprimir a la pantalla, comparables a write o read de PASCAL, (stdin y stdout) son scanf y printf. Las siguientes son porciones del manual en linea de UNIX sobre scanf.
scanf(3) UNIX Programmer's Manual scanf(3) NAME scanf - Converts formatted input LIBRARY Standard I/O package (libc.a) SYNOPSIS #include <stdio.h> int scanf ( const char *format [ , pointer, ... ] ); PARAMETERS format Specifies the format conversion. stream Specifies the input stream. string Specifies input to be read. pointer Points to location to store interpreted data.
Note que uno de los parámetros es un puntero. Esta es la única instancia en que trabajaremos con punteros por ahora. Incluyo una breve explicación al final de este manual.
DESCRIPTION The scanf(), fscanf(), and sscanf() functions read character data, interpret it according to a format, and store the converted results into specified memory locations. If there are insufficient arguments for the format, the behavior is undefined. If the format is exhausted while arguments remain, the excess arguments are evaluated but otherwise ignored. These function reads its input from standard input (stdin). The format parameter contains conversion specifications used to interpret the input. The pointer parameters specify where to store the interpreted data. If there are insufficient arguments for the format, the behavior is undefined. If the format is exhausted while arguments remain, the excess arguments are evaluated as always but are otherwise ignored. (...)
3.11. Ejemplo. Considere el siguiente programa.
/* ejemplo de programa en C: una manera tonta de
computar exponentes */
#include <stdio.h>
main()
{
int i, n;
float x, acum;
printf("Computo de exp.\nEntre el valor de x: ");
scanf("%f", &x);
printf("Entre el exponente: ");
scanf("%d", &n);
for(i=0, acum=1.0; i < n; i++) acum *= x;
printf("El resultado de %f^%d es %f.\n", x, n, acum);
}
Una ejecución del programa anterior nos daría algo así . Aquí lo que está en tipografía Times, esta que está leyendo, es lo que entra el/la usuario.
Computo de exp. Entre el valor de x: 5.1 Entre el exponente: 4 El resultado de 5.1000E01^4 es 6.765201E02.