La gestación del lenguaje C

2009/07/28

Dennis y Brian trabajaban en una versión de Pascal llamada "A".

Cuando se dieron cuenta de que alguien intentaba crear programas usando A, rápidamente añadieron características crípticas que lo convirtieron en B, BCPL, y finalmente C.

Se detuvieron cuando lograron compilar sin errores la sintaxis siguiente:

for(;P("\n"),R=;P("|"))for(e=C;e=P("_"+(*u++/
8)%2))P("|"+(*u/4)%2);


Visto en The Smoke Sellers.

Estructuras de control

2009/07/21
Bucles: for

En programación, un bucle es una estructura de control que permite que se ejecuten un conjunto de instrucciones repetidamente, según una condición. Si se requieren ejecutar sentencias más de una vez, for u otros bucles son una gran utilidad.

Lo primero que se dice sobre una estructura de control es su forma general. Para for es

for(inicialización; condición; incremento)
{
|||||||sentencia0;
|||||||sentencia1;
|||||||...
|||||||...
}

Ahora: ¿inicialización de qué? ¿Incremento de qué?

La inicialización suele ser la asignación de un valor inicial para una variable que llamaremos "variable de control". El incremento es cuánto cambiará la variable de control para el próximo ciclo. La condición es simplemente una expresión relacional que define cómo y cuándo terminará el bucle. Observe que cada una de las partes que conforman el bucle for son separadas por un punto y coma (;).

Reflexiónelo un momento: sin bucles, la manera de imprimir los números 1 a 500 -por decir algo- sería escribiendo printf() o funciones parecidas ¡quinientas veces! Lo cual, además de poco práctico y nada elegante, será un seguro desastre: lo reto a demostrar que tiene la paciencia necesaria. Empleando for, esa misma tarea se vería reducida a unas cuantas líneas de código, siguiendo estos pasos:

1) Declarar la variable de control como entero. Por ejemplo: int x.
2) Construir la estructura for. Asignarle 1 a x como valor inicial (x=1).
3) Establecer la condición. Por ejemplo, mientras x sea menor o igual que 500, que se ejecuten las sentencias (x <= 500).
4) Definir el incremento. De cinco en cinco (x+=5), de dos en dos (x+=2), de uno en uno... (x++ ó x+=1); como usted lo decida.

for(x = 1; x <= 500; x++) printf(" %d", x);

Nota: expresiones como x+=2 son pequeñas abreviaciones. En este caso, x += 2 es equivalente a x = x + 2 (x igual a sí mismo más dos). No está limitado a la suma o acumulación; también son posibles asignaciones como -=, *=, etc...



Tabla del 11


La instrucción for de C, comparada con sus equivalentes en otros lenguajes, es por mucho más flexible y adaptable, ya que se le puede escribir de muchas maneras: como al trasladar la inicialización fuera de él; obviar el incremento -aunque trasladándolo a otro sitio, de lo contrario provocaría un bucle infinito- o cosas aún más extravagantes: ¡inicializar varias variables en el mismo bucle for!

El siguiente ejemplo es perfectamente válido.

for(x = 0, y = 20; x <= y; x++, y--)
|||||||printf(" %d %d \n", x, y);

Lo que produciría en pantalla la siguiente salida.




Haga sus pruebas.

Estructuras de control

2009/07/20
Condicionales o de decisión: switch

Implícitamente switch manifiesta elegancia. Ya que, una vez que se conoce, uno se da cuenta que es una forma práctica y muy ordenada de evaluar constantes.

Su forma general es

switch(variable) {
|||||||case constante0:
|||||||||||||||sentencias;
|||||||case constante1:
|||||||||||||||sentencias;
|||||||case constante2:
|||||||||||||||sentencias;

||||||||||...

|||||default:
|||||||||||||||sentencias;
}


En primer lugar, switch evalúa una y sólo una variable. Según qué constante esté almacenada en la variable será el "case" cuyas sentencias se habrán de ejecutar.
Si ningún "case" corresponde a la constante de la variable evaluada, y existe "default" -cuyo uso es completamente alternativo: puede o no presentarse-, serán las sentencias de "default" las ejecutadas.

Como verá, su uso es muy simple. Ponga atención al siguiente ejemplo.



Todos los "case" ejecutarán la misma sentencia, sin importar cuál sea el que contenga la constante correcta. En este caso, mientras la variable sea igual a una vocal, los "case" correspondientes retornarán el valor 1, para Verdadero. Si la variable no almacena una vocal, será "default" la culpable de retornar 0.

Ahora, si lo importante fuese evaluar un caracter en particular, habría que recurrir al uso de la sentencia break por cada "case".
break utilizada dentro de cada "case" provoca que el control del programa salga del bloque switch y continúe justo después donde éste acaba. Sin break en cada "case" -como en el ejemplo-, todas las sentencias se ejecutarán.

Repetición simplificada: cada "case" se ejecutará uno detrás de otro, a menos que exista la sentencia break entre ellos.

En programas de sólo texto (como todos los ejemplos mostrados aquí) switch es a menudo empleada para comprobar una entrada en el teclado y ejecutar las sentencias que correspondan; pudiendo llamar a otras funciones y/o procedimientos adecuados. Observe un menú como ejemplo.



Nota: Tratándose de un caracter individual (tipo de dato char) siempre se escribirá en comillas simples: case 'a', case '5', etc.

En el fondo, switch es una escalera if - else if muy ordenada que sólo evalúa igualdades; nunca expresiones relacionales ni lógicas.

Estructuras de control

2009/07/18
Condicionales o de decisión: if

Este tipo de estructuras permiten decidir qué líneas de código se ejecutarán, según una condición concreta. En español, la sentencia if es parecida a la frase "Si... [Condición], entonces... [Resolución]".

La forma más simple de la sentencia es

if(condición) sentencia;

ó

if(condición)
|||||||sentencia;

Cuando se escriba más de una sentencia dentro de la misma estructura deben agregarse llaves de inicio y fin.

if(condición) {
|||||||sentencia0;
|||||||sentencia1;
|||||||sentencia2;
}

Si la condición es cierta, las sentencias se ejecutarán. De lo contrario, el programa continuará justo después del final del bloque if.

En ocasiones es conveniente analizar varias condiciones. Asímismo es conveniente tener una salida o escapatoria para cuando ninguna condición se cumple. Es entonces cuando se emplea la escalera if-else if.

if(condición)
|||||||sentencia;
else if(condición)
|||||||sentencia;
else if(condición) {
|||||||sentencia0;
|||||||sentencia1;
|||||||sentencia2;
}
else if(condición)
|||||||sentencia;
else |||||||||||||||||||||//Se cumplirá sólo si las anteriores son falsas
|||||||sentencia;|||||

Una condición es una expresión lógica; con operadores relaciones y lógicos, tal como 5 <= 2 y 42 == 20 + 20 + 2, falso y verdadero, respectivamente.

Las condiciones se evalúan de arriba a abajo. Cuando una condición sea cierta se ejecutan las sentencias asociadas a ella y se ignora el resto de condiciones. Si ninguna es cierta, y existe 'else', será 'else' la que se ejecute. Si no hay 'else' y todas las condiciones fallan, no ocurrirá ninguna acción.

Ejemplos:







Encuentre las diferencias.

Algunas consideraciones...

sobre printf() y scanf().

Si ha practicado un poco se habrá dado cuenta que cuando algunas cifras en coma flotante no necesariamente requieren todos los dígitos de precisión, al imprimir se convierten en una invasión de ceros. Obviamente, los ceros rellenan los dígitos de precisión que no se han utilizado.

Debe saber que los códigos de formato pueden tener modificadores.

En el caso mencionado, para especificar el número de decimales a imprimir de un número en coma flotante, sólo basta colocar cuántos dígitos a la izquierda, cuántos a la derecha. "%.3f" y "%2.2f" son ejemplos de esto. El primer ejemplo significa que hay una restricción sobre los dígitos a la derecha del punto, digamos, del número 3.330000, que al imprimirse se mostraría 3.330.

En números enteros y en cadenas el modificador actuaría diferente. La cadena "preprocesador", impresa con el siguiente modificador "%4.6s", se mostraría como "prepro", siguiente la lógica de "al menos 4 caracteres pero menos de 6".


*******************************************************************************


De scanf() sólo hay que mencionar unos detalles.

1) Para leer cadenas de caracteres no es necesario el ampersand (&) junto a la variable.

char nombre[20];
...
...
scanf("%s", nombre);

2) Puede incluir modificadores. En el último ejemplo, si el modificador fuera "%10s", se leerían sólo hasta 10 caracteres para nombre.

3) scanf() no sólo lee una variable a la vez. 'scanf("%c%c%c%c%c", &e&d&c&b&a);', por ejemplo, asigna un caracter a 5 variables.

4) Con caracteres ajenos en la cadena de control (letras, espacios en blanco, etc) ocurrirán comportamientos especiales.

Por ejemplo, "%s ", haría que luego de introducir un dato fuese necesario introducir cualquier caracter diferente a " " (espacio en blanco).

scanf()

2009/07/12
Usted ya conoce las variables; sabe cómo declararlas, cómo asignarles valor y cómo utilizarlas; seguramente imagina cómo tratar algunos datos y, asimismo, mediante printf() darles salida. ¿Pero y la entrada de datos...? ¡Ajá!

scanf() es una función, aunque no la única, de entrada mediante teclado. Al tratarse de una función muy general, es posible leer distintos tipos de datos. Es equivalentemente lo contrario a printf(). ¿Qué significa? Que puede usted, mediante scanf() leer datos en vez de escribirlos en pantalla; es decir, darle un valor a una variable EN TIEMPO DE EJECUCIÓN.

Por ejemplo, un programa que sume dos números 1) le pedirá los datos de entrada -los números a sumar-, 2) los procesará, y 3) los mostrará.

¿Está listo para escribir eso? ¡Seguro!

Forma de scanf():
scanf("cadena(s) de control", lista de argumentos);

Al igual que printf(), scanf() emplea códigos de formato para asegurarse de que los datos introducidos son coherentes con el tipo de variable. La lista de argumentos correspondería a la(s) variable(s) en las que se guardarán los datos.

Códigos de control para scanf():
%c - lee un único caracter
%s - lee una cadena
%d - lee un entero decimal
%e - número en coma flotante
%f - número en coma flotante
%h - entero corto
%o - número octal
%x - número hexadecimal

Ejemplos:
/* lee un número entero */
scanf("%d", &var);

/* lee tres caracteres consecutivos y los almacena en 3 variables */
scanf("%c%c%c", &var1, &var2, &var3);

La razón por la que cada variable está acompañada de un ampersand es que las variables que reciben valores se pasan, a través de scanf(), por sus direcciones, creando una llamada por referencia. Es probable que ahora esto sea confuso. De todos modos es un tema que se tratará oportunamente: punteros. Sin embargo, es conveniente aclarar que cuando se trate de una cadena NO se empleará el ampersand, ya que una cadena al ser un array de caracteres, ya está pasando su dirección con el primer elemento del arreglo. Esto también será tratado en otro momento.

Programa que suma 2 números:


Versión poco elegante. Lo reto a mejorarla.



Nota: En C, los archivos de código fuente deben llevar la terminación punto c (.c). Recuerde que se trata de texto plano; por favor no piense en utilizar algo diferente a un editor de TEXTO PLANO.

Jugando con los operadores aritméticos, usted podría muy pronto tener escrita las funciones para una calculadora simple. Después, cuando conozca las estructuras de control, podrá hacer más robusto su programa. ¡No deje de practicar! ¡Escriba!

printf()

De imprimir con formato. Esta función, del archivo stdio.h que usted ya conoce, se merece una entrada exclusiva por ser la función de salida más común y compleja. Además hay unos puntos importantes que el principiante debe conocer.

En esencia, printf() tiene la forma:

printf("cadena(s) de control", lista de argumentos);

Note el uso de comillas para la primera parte de la función, y note también que hay "argumentos exteriores" o lista de argumentos que NO están entre comillas, a menos que se trate de una cadena de caracteres como argumento. Para explicar esto es necesario que usted conozca ya los tipos de datos de las variables y que sepa distinguir uno de otro. Si no es así, regrese a ese tema y repase.

Ahora: la cadena de control es una cadena de códigos que indican cómo tratar al "argumento exterior" que le corresponda. Primero veamos un ejemplo y después pasemos a la explicación.




Lo que produciría esta salida en pantalla:




¿Ve una correspondencia entre cadenas de control y los argumentos exteriores?

El primer código de control (que en este caso es para un caracter: %c) imprime 'a', que en la lista de argumentos tiene comillas simples por ser un solo caracter. El segundo código imprime 'b', el tercero imprime "quizá sea" y su código es %s por tratársele como string (esto es una cadena de dos o más caracteres). El cuarto código de control cambia, se trata de %d, que es el código de formato para números enteros. El último código de control imprimirá un número en coma flotante, agregando ceros al final (más tarde se mencionará cómo evitar esto). En el ejemplo, al final se ha agregado un caracter de nueva línea (/n); esto es para que el prompt no termine pegado al mensaje mostrado.

Lista de códigos de control:
%c - caracter sencillo
%s - cadena de caracteres
%d - decimal
%e - notación científica
%f - decimal en punto flotante
%g - %e ó %f, lo que sea más corto.
%o - octal
%u - decimal sin signo (del tipo de dato unsigned int)
%x - hexadecimal

Nota: debe haber igual número de códigos de control que de argumentos exteriores en la función printf().

No está demás decir que para imprimir el caracter '%' debe escribir %%. De esta manera evita la ambigüedad y permite que el compilador sepa que no es un código de control.

Por favor juegue, experimente y pruebe lo que puede hacer con printf().