Conway's game of life

2009/12/07
He aquí un programa muy famoso y muy simple: el juego de la vida. Para quien no lo conozca, comience a leer por aquí.






El juego de la vida es un gran ejemplo del comportamiento emergente -un tema muy interesante, por cierto-. Si es la primera vez que se ve y si no se han asimilado los conceptos probablemente no tendrá mucha gracia, pero una vez que se entiende ¡no se puede dejar de ver!

En el código de más abajo -escrito en C, obviamente (aunque sospecho que no es la mejor decisión, sin embargo)- se puede modificar fácilmente el caracter que representa a las células vivas al igual que el caracter que representa a las células muertas. Asimismo, es posible modificar las dimensiones del "tablero" sin mayor esfuerzo. Sin embargo, está limitado a los patrones que se codifiquen señalando manualmente ciertos puntos en una matriz (arreglo de arreglos), lo que no es un gran problema si se tiene tiempo para escribir nuevos patrones... Por cierto, descubrir patrones interesantes es una parte realmente divertida del juego.

En cualquier caso, más allá de la torpe implementación que yo haya hecho, invito a quienquiera que se interese por esto a que escriba su propia versión, que juegue a cambiar las reglas y a descubrir la vida.






El código aquí: el juego de la vida.

K&R

2009/12/06
Navegando me encontré con un libro fundamental que, lo juro, nunca he tenido en las manos. Hasta ahora.

Yo conocí C mediante mis apuntes de otros lenguajes y de pequeños manuales en la red -como éste blog-, y de fotocopias de un libro arcaico y poco claro de Herbert Schildt. Pero para conocer el lenguaje sin más esperas, al ritmo que a cada uno le convenga, y sobre todo, explicado por los autores (Ritchie es  un grande entre los grandes de esta ciencia), nada mejor que el libro.






Gracias a la web LuChOeDu, que lo pone al alcance de todos.

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().

Operadores

2009/07/11
En C hay cuatro clases de operadores:
  • Aritméticos
  • Relacionales
  • Lógicos
  • De bits

Operadores aritméticos

Todos sin excepción conocemos desde nuestros primeros años los operadores aritméticos: suma, resta, multiplicación, división. En C, estos operadores no cambian respecto a lo que ya conocemos. Empero, hay que decir que se agregan unos operadores más.

+
-
*
/
%
++
--

El operador % (a veces llamado mod) se utiliza para conocer el residuo en una división.
Así, 5%2 = 1 (se lee "cinco mod 2 igual a uno").

++ y -- se utilizan para incrementar en 1 o disminuir en 1 a una variable. Se escriben de la siguiente forma:
variable++ ó ++variable

La sutil diferencia entre colocar ++ o -- antes o después del nombre de la variable, se explica así: si el operador precede al operando, se realiza primero la operación (sumar o restar 1) antes de utilizar el valor del operando. Es decir, si x = 5, ++x hará que x tome el valor 6 directamente. En cambio x++ hará que x tome como valor 5, luego 6, 7, 8..., y así hasta que se quiera.

Usualmente, la precedencia es:
++ --
* / %
+ -

Significa que en una expresión, digamos, algo como 5 * 5 * 5 + 4 / 2, primero se resolverían las multiplicaciones, luego la división, y finalmente se sumaría lo uno a lo otro. En ocasiones es conveniente utilizar paréntesis para manipular la precedencia.


Operadores relacionales y lógicos

Los operadores relacionales tratan de las relaciones -eso fue redundante- de un número con otro (5 > 2, que produce Verdadero), mientras que los lógicos tratan de la unión de las relaciones (5 > 2 y 20 > 19, es decir verdadero y verdadero = verdadero).

En C, 0 representa falso, y cualquier otro número, verdadero.

Lista de operadores relacionales:

> (mayor)
>= (mayor o igual)
< (menor)
<= (menor o igual)
== (igual*)
!= (distinto)

"==" es para comparaciones, mientras que "=" es para asignar un valor. Cuidado con esto.

Lista de operadores lógicos:

&& (And)
|| (Or)
! (Not)

Precedencia:

!
> >= < <=
== !=
&& ||

Una expresión compuesta en donde existan operadores aritméticos, relacionales y lógicos, se revolverá precisamente en ese orden.

Ejemplo: 2 + 3 < 5 * 6 && 99 - 100 == 5.

Se resolvería ordenadamente del siguiente modo.

1) 5 < 30 && -1 == 5
2) Falso (0) && Falso (0)
3) Falso AND Falso produce Falso como respuesta a toda la expresión.

Sobre los operadores de bits se hablará adelante. Muy, muy adelante...

Variables

Temas de la entrada:
  • Variables
  • Identificadores de variables
  • Tipos de datos
  • Expresiones

Variables

Si no lo sabe, sepa que uno de los dispositivos más importantes de un ordenador es la memoria de acceso aleatorio (RAM), pues es donde temporalmente se almacenan programas y datos. De no ser así, el procesador no podría "ver" ni datos ni programas, sencillamente porque el procesador sólo se comunica con la RAM (y, secundariamente con cache RAM).

La memoria se divide en posiciones de memoria que almacenan tanto datos como programas. En programación, una variable sirve para acceder a esas posiciones de memoria. Por tanto, he aquí una definición técnica de variable:

Una variable es la referencia lógica a una posición física de RAM.

De momento no se frustre si no comprende bien lo dicho hasta aquí.

Con las variables almacenamos distintos valores; les damos un nombre único (identificador) y las empleamos cuando lo requiramos. Obviamente, una variable por ser variable puede tomar distintos valores en el mismo programa. De lo contrario, se trataría de una constante.

Los valores (números, caracteres, etc) almacenados en memoria permanecerán allí hasta que 1) liberemos la memoria, 2) cerremos el programa donde se utilizaron las variables, ó 3) apaguemos la máquina.


Identificadores

Un identificador es el nombre que se le da a una posición de memoria; es decir, a una variable. La razón de esto, es que esas mismas posiciones ya tienen un identificador interno, sólo que al tratarse de números en hexadecimal, pueden ser difíciles de recordar, por lo que es más práctico que cada quien defina el nombre de sus posiciones de memoria.

Ahora bien, existen reglas para construir identificadores:

  • Que inicien con un caracter del alfabeto o con guión bajo (_)
  • Si se inicia con guión bajo, al menos debe tener un caracter más.
  • Después de eso, sólo pueden contener caracteres del alfabeto, números y guiones bajos. Nada de caracteres especiales.
  • NO pueden contener ESPACIOS. Se trata de un único conjunto de caracteres.
  • Por último, no pueden contener el mismo nombre que las palabras reservadas del lenguaje, tampoco el mismo nombre que las funciones tanto escritas por usted como las de las librerías.
Ejemplos:
a
Pi
abono_cuenta
_iniciales
num1

En C, las mayúsculas y minúsculas son diferenciadas. Por lo que _iniciales e _INIciales serán dos identificadores distintos.



Tipos de datos

Una variable podrá almacenar distintos datos, sí, pero nosotros debemos especificar cuáles.

En C existen los siguientes tipos de datos:
char - para un caracter
int - (número) entero
short int - entero corto
long int - entero largo
unsigned int - entero sin signo
float - coma flotante (números como 3.14159265)
double - coma flotante en doble precisión

Así, si lo que se quiere es una variable para colocar el número 22099, lo ideal sería declarar la variable como int.

Cabe aclarar que cada tipo de dato tiene un consumo específico de bits, y un rango determinado.





Declaraciones

Declarar una variable no es más que indicar qué tipo de dato debe tener.

Por ejemplo:

char letra;
- Donde letra es el identificador, precedido del tipo de dato.

float e = 2.718281;
- Donde la variable e es coma flotante, y en la misma declaración se le asigna un valor.

Asignar un valor durante la declaración de la variable depende de la situación en el programa. Por ejemplo, en una lista de argumentos nunca verá una asignación.



Expresiones

Ahora que sabe cómo declarar y darle valores a una variable, es tiempo de comenzar a hacer expresiones.

Una expresión es una combinación de variables, constantes y operadores (estos últimos serán detallados más adelante).

2*4
x = x * 3 - 15
23 < 4

Recuerde que dentro de un programa C, cada sentencia termina con punto y coma (;).

Elementos básicos

2009/07/10
A continuación, el esqueleto de un programa:

  • Comentarios
  • #include y archivos de cabecera
  • La función main()
  • El caracter de nueva línea (\n)
  • Puntos y comas, llaves
  • Compilación

Ejemplo:
-----------------------------------------------------

/* Esto es un comentario */
// También es un comentario

#include '<'stdio.h'>' ||| // <---NO lleva comillas.
main()
{
|||||||
printf (" ¡Mirad! ¡Mi primer programa! \n");
|||||||return 0;
}

-----------------------------------------------------

Comentarios

Lo primero que vemos al examinar el ejemplo son las 2 formas de escribir comentarios en C. La primera, con un signo de apertura (/*) y uno de cierre (*/). La segunda, asimilada de C++, consta solamente de dos barras.

El motivo y la utilidad de los comentarios es principalmente como herramienta de aclaración, recordatorio, o apunte sobre el programa. Es decir, como documentación propia. Existe la posibilidad de que tiempo después de haber desarrollado un programa, el programador se olvide por completo de él; cuando el programador retome su programa, los comentarios que haya hecho en el código le harán más sencillo entender y recordar exactamente qué había hecho. Otra razón sería explicar brevemente lo que alguna parte del código hace. Esto ayuda mucho cuando alguien más tiene que ver el código. Incluso al depurar, el empleo de comentarios es por demás útil.

Los comentarios no afectan sobre el funcionamiento del binario ejecutable ni sobre el tamaño del mismo, así como tampoco afectan de intromisión al código. La razón es que el compilador ignora todo lo que haya entre las marcas de comentarios (/* */), o entre marca y fin de línea (// ).


#include y archivos de cabecera

En el ejemplo, la primera línea valiosa de código es #include

La instrucción #include le comunica al preprocesador de C (un programa llamado por el compilador) que hay que buscar un archivo y colocar el contenido en el lugar correspondiente. stdio.h es ése archivo. El lugar de búsqueda obedece a la indicación que rodea a stdio.h. Si los signos son comillas dobles ("stdio,h"), el preprocesador buscará el archivo en el directorio actual. Por el contrario, si son llaves lo que rodean el nombre del archivo (), el preprocesador buscará en un directorio diferente del actual (/usr/include y c:\Compilador\include son 2 ejemplos).

stdio.h es quizá uno de los archivos fundamentales de un programa: sus funciones se encargan de la entrada y salida. Provee de funciones como printf(). Su nombre significa "standard input-output header file". Y sí, fue escrita por el desarrollador del compilador (por lo que es posible que en distintos compiladores haya diferencias mínimas).

Función main()

Es la función que se llama cuando se ejecuta el programa. Cada programa debe tener una y sólo una función principal, pues si hubiera más de una, ¿por dónde comenzaría el programa? De todos modos el compilador detectaría un error como ése. Es la función de donde parte el funcionamiento del programa: al llegar a la llave que cierra la función, el programa termina de ejecutarse (aunque hay otros modos de salir del programa).

Esta función puede estar colocada al principio, en medio o al final del fichero donde se escribe el código. Da igual. Sin embargo, por claridad es recomendable colocar la función al principio.

En el ejemplo sólo existe la función main(). En programas más complejos es conveniente dividir las tareas y emplear mas funciones.


Caracter de línea nueva (\n)

Una de las cosas que más escribo es el símbolo '\n', siempre con printf(). Lo menciono ahora porque su uso es frecuente y sería útil familiarizarse con él.

Básicamente, y según cómo se emplee, hace 2 cosas: generar el fín de línea y generar una línea nueva.

Ejemplos:

-----------------------------------------------------------------------
// 1)
#include '<'stdio.h'>'
main()
{
|||||||printf("\n\n\n Apuesto a que el prompt terminará pegado a mí");
}


Ese código se mostraría así:


Tres líneas vacías. Al final se junta con el prompt.
-----------------------------------------------------------------------

// 2)
#include '<'stdio.h'>'
main()
{
|||||||
printf(" Hola, enfermera \n\n");

}

Visualización:

Muestra el texto y termina la línea. Luego, línea vacía. Observe el prompt ahora.

-----------------------------------------------------------------------


Puntos y comas y llaves

En C, la mayoría de las sentencias terminan en punto y coma.

x=y;
a = b + c + d * 5;
sqrt(num);

Excepto al declarar una función, así como tampoco en la llave inicial ni final de cualquier función o estructura.

int calculo()
{
|||||||x=y; a = b + c + d * 5; sqrt(num);
}

Observe cómo las sentencias fueran colocadas intencionalmente en la misma línea. Esto es válido, ya que la función de los punto y coma (;) es precisamente reconocer el final de una sentencia. Aunque... No es muy agradable leer código escrito así.

Nota: ¡Recuerde la indentación! Por favor escriba con sangría.


Compilación

Por lo pronto, y tratándose de archivos pequeños, este proceso es bastante sencillo una vez que se conoce.

En mi caso, desde la línea de comandos hago esto:
1) Me coloco en el directorio donde esté guardado el código fuente
2) Llamo al compilador, escribiendo el nombre del programa como parámetro.
3) Ejecuto.
4) Si es necesario, depuro y vuelvo al paso 1.

Al final, lo que escribiría sería algo como:
$ gcc NombrePrograma.c -o NombreNuevo.loquesea

Si usted utiliza otro compilador, averigüe cuáles son los detalles y especificaciones de su compilador y los pasos a seguir.

Estandarización

2009/06/11


Por muchos años, el estándar para C fue el llamado K&R (por las iniciales de Kernighan y Ritchie), debido al libro que contenía las "especificaciones primigenias" del lenguaje. Sin embargo, con el tiempo aparecieron cambios no oficiales que no se encontraban documentados por el estándar K&R.

Tal situación suponía una pérdida de la portabilidad del código de los programas, así que las acciones por evitar esto vinieron por parte de un grupo de vendedores de compiladores y de desarroladores de software, quienes en 1983 pidieron al Instituto Nacional Estadounidense de Estándares (ANSI, por sus siglas en inglés) crear un estándar para el lenguaje.
El instituto aprobó la petición, formó un comité técnico y trabajo en ello. El resultado: a finales de 1989 se aprobaba el estándar ANSI C, también llamado C89 y C90.

Se ofrecían mejoras al K&R, como el grupo de funciones comunes que pasó a ser la librería estándar. Ahora todos los compiladores de C deben de soportar esta librería, junto con especificaciones del propio compilador.

Porteriormente, se adaptaría de nuevo una estandarización sobre el ANSI C, esta vez llamado C99.

Funciones

2009/06/10

Una manera de describir un programa escrito en C -y en general, en cualquier lenguaje estructurado-, es como una pared de ladrillos, donde, por supuesto, las funciones son esos ladrillos. No hablamos de algo parecido a construir una ciudad de juguete con piezas de lego, sino más bien de una manera lógica de ordenar líneas de código.

Es importante entender que

1) Un programa es una colección de funciones
2) Hay que crear las funciones y luego juntarlas
3) Existe una función principal (main), que es de donde parten todas las demás

Por definición, una función es una subrutina que contiene sentencias, que lleva a cabo una tarea, y que retorna un valor. Cada función tiene nombre -el que uno quiera siempre que no sea 'main'- y una lista de argumentos (los valores que recibe desde una función "llamadora").

La razón por la que el nombre 'main' está apartado, es porque se presupone que habrá una función main (principal), que es la función que inicia un programa, además de ser la que lleva el orden.



Forma de las funciones

Una forma general que describe una función podría ser ésta:



Donde, en primer lugar, aparece el nombre y la lista de argumentos entre paréntesis. En segundo lugar, se declara el tipo de dato (y los modificadores, si los hay) de las variables consideradas en la lista de argumentos. Y las llaves marcan el principio y el final del cuerpo de la función, o mejor dicho, del código escrito para la función.

'return' es una sentencia muy frecuente, que fuerza el retorno desde una función. Mediante return se indicará el valor que tome la función.

Ejemplo:

multiplicacion(a, b)
int a, b;
{
//sangríareturn(a*b);
}

Otras generalidades

2009/06/06
C es un lenguaje estructurado

Es decir, utiliza bloques. Un bloque es un conjunto de sentencias relacionadas. La idea de subrutinas y variables locales sólo puede ser posible en un lenguaje estructurado.

Incluso es factible compilar por separado las rutinas, sin necesidad de que formen parte del programa -pero pudiéndose referenciar. Y es ése el concepto de librería-, de tal manera que se puedan crear librerías con funciones útiles a las que se puedan acceder desde cualquier otro programa por escribir.



Compilador

A diferencia de los lenguajes interpretados, en C es un compilador el que genera el código objeto (también código binaro, o código máquina: ceros y unos, pues) a partir del código fuente. Esto es: traducir de una vez y para siempre las instrucciones del programador, entendibles por el humano, a instrucciones que el ordenador ejecute. Después de esto, el código fuente no tiene relevancia sobre la ejecución de un programa, pudiéndose alterar sin consecuencias para el código objeto.

A menudo se utilizan los términos tiempo de compilación y tiempo de ejecución. Se refiere a tiempo de compilación cuando se trata de procesos que ocurren al compilar (normalmente, cuando el compilador revela errores sintácticos u otros). Se refiere a tiempo de ejecucuón al tratarse de lo que ocurra cuando el programa se está ejecutando (y es aquí cuando es posible distinguir fallos lógicos en el programa).



Para investigar:

-Lenguaje estructurado y lenguaje interpretado
-Intérprete y compilador

C como lenguaje de medio nivel

2009/06/04
  • Que C se considere como un lenguaje de medio nivel no quiere decir que sea peor, poco útil o menos poderoso que los de alto nivel. Nada más lejos del significado real de la expresión.
  • "Medio nivel" significa, por un lado, que es similar a los lenguajes de bajo nivel (ensambladores), por lo menos como representaciones simbólicas (instrucciones) del auténtico código máquina (ceros y unos), y por el otro lado, que contiene elementos de un lenguaje de alto nivel (estructuras de control, de decisión, etc).
  • Básicamente, un lenguaje de bajo nivel es el que está orientado especialmente a una máquina en particular (un procesador en específico, por ejemplo), mientras un lenguaje de alto nivel está orientado al programador, facilitando la lectura y comprensión del código, casi como leer en inglés. Asímismo, la portabilidad entre plataformas es -más o menos- proporcional al nivel del lenguaje empleado.

Para investigar:

-Lenguaje de medio nivel
-Lenguaje ensamblador
-Portabilidad