El libro de Kernighan y Ritchie sobre el lenguaje de programación en C, introdujo la práctica de que el primer ejemplo sea un programa minimalista, normalmente imprimir algún texto simple, como "hola mundo" en la pantalla:
Para crear un programa en C, se debe crear un archivo de texto, por convención con extensión .c
, por ejemplo, hola.c
. Luego se debe invocar el compilador en línea de comandos. Por ejemplo, si se utiliza el GNU Compiler Coleccion (GCC):
gcc -Wall -std=c99 -o hola hola.c
El comando anterior indica al compilador de C, gcc
, que compile el archivo fuente hola.c
y genere un ejecutable hola
. Por convención en Unix los ejecutables no tienen extensión, en Microsoft Windows se querrá hola.exe
. El parámetro -Wall
indica al compilador que despliegue todas las advetencias (warnings) que pueda encontrar. Un buen programa debe generar 0 advertencias. El parámetro -std=c99
indica al compilador que habilite los cambios que se hicieron al estándar C en el año 1999.
Tanto C como C++ son lenguajes multi-paradigma. El paradigma más influyente en C es la programación procedimental, mientras que en C++ la programación orientada a objetos. En el paradigma procedimental, un programa consta de muchas funciones que se llaman entre ellas. No hay objetos ni clases, las funciones son "métodos libres", también llamadas "funciones libres".
La estructura de un programa en C consta de funciones, declaraciones de variables globales y declaraciones de tipos de datos. Pueden aparecer en cualquier orden, mientras se cumpla la regla de que algo puede ser usado sólo si está declarado antes. Un prototipo puede utilizarse cuando se necesita evadir esta regla. Un prototipo de una función consiste en la declaración de la función sin su cuerpo. Sirve para decirle al compilador que la función existe en algún otro lugar del código fuente, además de los parámetros que necesita para ser invocada.
La función libre que lleva por nombre main()
es especial. Ella inicia la ejecución de un programa ejecutable. Debe retornar un entero, que indica al sistema operativo si la ejecución de nuestro programa fue exitosa (con un 0), o que hubo un error (con un número distinto a 0).
Todo símbolo debe estar declarado. ¿Dónde está declarado printf()
? Lo está en el archivo stdio.h
. Al encerrarlo entre paréntesis angulares #include <stdio.h>
, le indica al preprocesador que debe encontrar este archivo en las carpetas donde está instalado el compilador.
El uso de variables globales es una práctica común en C, sin embargo, se considera una mala práctica de programación. Más adelante se introducirán formas de evitar su uso. La línea 25 utiliza una constante literal real (-0.0001
) en una condición, lo cual es completamente válido en C, ya que cualquier valor distinto de 0 se considera verdadero.
La línea 27 hace un recordatorio de que el programador siempre debe tener cuidado con el operador de división. Si sus dos operandos son enteros, el resultado será el cociente de la división entera.
El tamaño y capacidad de los tipos de datos primitivos en C/C++ dependen de la arquitectura a la cual el compilador esté generando código:
Tipo | Modificadores | 32 bits | 64 bits | Ejemplos | ||
---|---|---|---|---|---|---|
Tamaño | Rango | Tamaño | Rango | |||
bool 1 |
1B == 8bits | {false, true} |
||||
char |
[signed] char |
1B == 8bits | {-27, ..., 27 - 1} == {-128, ..., 127} == {-128, ..., -1, '\0', ..., '~', 127} |
|||
unsigned char |
1B == 8bits | {0, ..., 28 - 1} == {0, ..., 255} == {'\0', ..., '~', 127, ..., 255} |
||||
int |
[signed] short [int] |
2B == 16bits | {-215, ..., 215 - 1} == {-32 768, ..., 32 767} |
|||
unsigned short [int] |
2B == 16bits | {0, ..., 216 - 1} == {0, ..., 65 535} |
||||
|
El preincremento o predecremento se realiza antes de ejecutar la sentencia. El posincremento o posdecremento se realiza después de haber ejecutado la sentencia. Si hay dos o más (pre|pos)(in|de)cremento se ejecutarán en el orden que aparecen. Siempre que se pueda se debe prefer el preincremento y el predecremento sobre el posincremento y posdecremento, por razones de eficiencia.
En Unix (el sistema operativo más influyente en todos los sistemas operativos modernos), todo recurso es un archivo. Para Unix el teclado, la pantalla, la impresora, la red, son archivos; como también lo son los archivos tradicionales almacenados en memoria secundaria (disco duro, unidades de estado sólido (SSD, solid state drive), discos ópticos (CD, DVD, blue-ray), cintas magnéticas, etc.). Por esta razón, cuando un programa en C está imprimiendo en la pantalla, realmente lo está haciendo en un archivo que está conectado con la pantalla. Si en lugar de la pantalla se quiere imprimir en un archivo de texto en disco, basta cambiar el archivo destino; el programador sigue utilizando las mismas funciones como lo haría con la pantalla.
Las funciones para entrada y salida de C se encuentran en el encabezado <stdio.h>
. Para imprimir texto con formato, se utilizan las funciones printf
que varían el destino donde se quiere imprimir: printf(fmt,...)
imprime en la salida estándar, fprintf(file,fmt,...)
imprime en un archivo cualquiera, sprintf(str,fmt,...)
imprime en un string en memoria. Todos reciben un parámetro fmt
que es una cadena con formato, que dentro puede tener cero o más especificadores de formato (iniciados por un % y terminados en una letra) que son puntos en la cadena donde se insertarán los parámetros opcionales que reemplazan los puntos suspensivos (...
) (que en C son un operador que indican una cantidad arbitraria de parámetros).
Las funciones análogas a las printf
son las scanf
, utilizadas para leer de un archivo: sea el teclado scanf(fmt,...)
, un archivo tradicional fscanf(file,fmt,...)
, o un string en memoria sscanf(str,fmt,...)
. Existe una diferencia importante: las funciones printf
reciben los valores en sus parámetros en modo sólo-lectura con el fin de imprimir copias en el archivo destino, mientras que las funciones scanf
requieren punteros a las variables con el fin de modificarlas, por esto, hay que anteponer un ampersand (&
) a los tipos de datos primitivos, para pasar la dirección de memoria donde está la variable y no una copia del valor de la variable.
Ejercicio. Modifique el programa de fecha y hora, para que imprima la cantidad años cumplidos que tiene la persona; y la cantidad de días que faltan para el próximo cumpleaños, a menos de que la persona esté cumpliendo años y en tal caso, la felicita. Estudie las funciones del encabezado <time.h>
.
El formateo de los parámetros se hace entre el %
y la letra que indica el tipo de datos del parámetro. Se pueden escribir números que indican a la función hacer un campo de tantos caracteres como indicados por ese número. Esto es especialmente útil para imprimir tablas. El siguiente ejemplo muestra las tablas de multiplicar de un número mínimo a un máximo indicados por el usuario en incrementos de 1.
Ejercicio. Modifique el programa de tablas de multiplicar para que estime automáticamente el ancho ideal para cada valor de la tabla. Indague sobre el especificador de ancho *
en la cadena de formato. Probablemente deba implementar una función que dado un número retorne la cantidad de dígitos que éste ocupa.
Ejercicio. Modifique el programa de tablas de multiplicar para que permita al usuario indicar no sólo el mínimo y el máximo, sino también los incrementos. Estos tres valores pueden ser números reales. Utilice los números de mayor precisión que provea su compilador. Permita al usuario también escoger el operador. Trate de dar un estilo profesional al formateo de la tabla resultado como se solicita en este enunciado de Principios de informática.