TEMA 1 : Conceptos básicos 1.0 Introducción En este segundo tema se describirá la estructura básica de un programa en lenguaje C así como la forma de visualizar distintos tipos de datos en pantalla. Se introducirán los conceptos de tipos de datos básicos y su utilidad. 1.1 El programa HOLA MUNDO Este programa se ha convertido en un clásico dentro de los libros de programación. Simplemente muestra en pantalla el mensaje HOLA MUNDO, esto que puede parecer muy tonto es algo fundamental puesto que si no sabemos imprimir mensajes ó datos en la pantalla difícilmente nuestro programa se podrá comunicar con el usuario que lo utilice. Mostraremos el programa y a continuación describiremos cada una de las instrucciones que lo forman. /* Programa : HOLA MUNDO */ #include <stdio.h> main() { printf ("\nHola mundo"); } Como podemos observar se trata de un programa muy sencillo. La primera línea es lo que se conoce como un comentario, un mensaje que el programa añade al código del programa para explicar o aclarar su funcionamiento o el de una parte de él. Los comentarios se pueden situar en cualquier parte de nuestro código y se considerará como comentarios cualquier mensaje que se encuentre entre los caracteres /* y */. Los "/*" y "*/" no son caracteres, sino símbolos o banderas. La siguiente línea es lo que se conoce como directiva del preprocesador, todos los compiladores de C disponen de un preprocesador, un programa que examina el código antes de compilarlo y que permite modificarlo de cara al compilador en distintos sentidos. En temas posteriores trataremos en profundidad estas directivas del preprocesador, pero para el desarrollo de los temas anteriores a este debemos conocer al menos la directiva #include. Las directivas se caracterizan por comenzar con el carácter # y se deben incluir al comienzo de la línea aunque es probable que esto dependa de la implementación del compilador con el que estemos trabajando. La directiva include permite añadir a nuestro código algún fichero de texto, de tal forma que la directiva es sustituida por el texto que contiene el fichero indicado. En general los ficheros que acompañan a esta directiva son ficheros .H (Header - Cabecera), en los que se incluyen definiciones de funciones que deseamos utilizar en nuestros programas, constantes o tipos complejos de datos. La librería stdio.h (STandarD Input/Output) con tiene las funciones estándar de entrada salida, y en ella se encuentra la función printf que utilizamos en nuestro programa. Como se observa en el código el nombre de la función a incluir debe ir entre los caracteres <...>. A medida que vayan surgiendo iremos indicando las funciones estándar que deberían incorporar todos los compiladores C y cual es su fichero de definición .H. En la siguiente línea nos encontramos con main(). Esto indica que aquí comienza nuestro programa, en realidad estamos definiendo una función (esto se indica con los paréntesis al final de la línea) pero esto lo discutiremos en temas posteriores. La función main (principal en inglés) siempre debe existir y contendrá el programa principal. Finalmente nos encontramos el programa principal, una sentencia printf entre llaves ({, }). Las llaves en C representan bloques, y encierran un conjunto de sentencias o instrucciones (lo que el computador ejecutará), considerando todas ellas como una sola, permitiendo una definición homogénea de los distintos bloques que constituyen el programa. En nuestro caso tenemos un sólo bloque que no es ni más ni menos que el programa principal, que en nuestro caso está compuesto por una sola sentencia (la línea que contiene el printf). Como se observa en el código las sentencias en C deben terminar con un punto y coma (;), #include <stdio.h> y main() no son sentencias, dan información sobre la estructura del programa, y por tanto no finalizan con un punto y coma. NOTA: La razón de que la línea "main()" no tenga un punto y coma ";" al final es debido a que la sentencia en sí termina al cerrar el corchete, no en que dicha línea proporcione información sobre la estructura del programa. De hecho, si el "main()" constituyese una línea de prototipo tendría un ";" al final. La función printf permite visualizar datos formateados en pantalla, es decir, permite indicar un formato como si de un impreso o formulario se tratase indicando donde se deben visualizar cada uno. En el siguiente tema cuando se introduzcan los tipos básicos de datos se comprenderá mejor ésto. Por ahora sólo nos interesa conocer que printf visualiza mensajes en pantalla. El mensaje debe ir entre comillas dobles (") y dentro de las comillas se puede mostrar cualquier secuencia de caracteres. El formato de esta función para este segundo tema será: printf ("mensaje"); En el siguiente tema, cuando expliquemos en profundidad la instrucción, ampliaremos esta definición. En nuestro programa observamos que el mensaje de texto que visualiza la instrucción printf comienza con los caracteres \n. Estos caracteres nos permiten algunas funciones especiales para controlar la forma de visualizar los mensajes, la más utilizada en \n que significa nueva línea, así nuestra sentencia printf ("\nHOLA MUNDO"); moverá el cursor (la posición de la pantalla donde actualmente se visualizan los datos) a una nueva línea situándolo a la izquierda de la pantalla y visualizará el mensaje HOLA MUNDO. Para finalizar con este punto se indicará las secuencias antecedidas por \ que se pueden incluir en una instrucción printf: +-------------------------+------+ | Nueva línea | \n | | Tabulador horizontal | \t | | Tabulador vertical | \v | | Backspace (<-) | \b | | Retorno de carro | \r | | Avance de página | \f | | Pitido (alerta) | \a | | Caracter \ | \\ | | Caracter ? | \? | | Caracter ' | \' | | Caracter " | \" | | Número Octal (ooo) | \ooo | | Número Hexadecimal (hh) | \xhh | +-------------------------+------+ Algunos comentarios sobre estos códigos. En primer lugar el primer grupo (hasta carácter \), eran utilizados para mover el cursor en terminales. Los terminales podían ser una pantalla o una impresora, esta es la razón por la que nos encontramos cosas como avance de página o retorno de carro. Los caracteres \ ? ' " son especiales puesto que se utilizan dentro del mensaje a visualizar para indicar como se visualiza, o sea, si escribimos \ el compilador buscará el siguiente carácter y si es alguno de los anteriores los visualiza sino corresponde con ninguno simplemente lo ignora, con lo cual no podríamos visualizar el carácter \. Otro tanto sucede con las comillas puesto que para C, las comillas representan una cadena de caracteres, si escribimos " en nuestro mensaje se considerará que éste termina ahí con lo que lo que se encuentre después no tendrá sentido para el compilador. Por último Número octal y Número hexadecimal nos permite introducir directamente el código numérico en octal o hexadecimal del carácter que deseemos visualizar, dependiendo del que esté activo en nuestro computador. En general el código utilizado para representar internamente los caracteres por los computadores es el código ASCII (American Standard Code for Information Interchange). En este segundo tema hemos aprendido a escribir programas que visualicen en pantalla mensajes utilizando la función estándar de la librería stdio. Antes de terminar unos breves comentarios. El lenguaje C diferencia entre mayúsculas y minúsculas con lo cual main, MAIN, MaIn, serían identificadores distintos para el compilador, en general en C se suele escribir todo en minúsculas a excepción de los mensajes a visualizar (cuyo uso depende del programador) y de las constantes, esto no tiene por que hacerse así pero digamos que se trata de una tradición de la programación en C. Las separaciones entre líneas también son arbitrarias y su única función es facilitar la legibilidad del código, sirviendo de separadores entre fragmentos de programa relacionados entre sí. Y esto es todo por ahora. 1.2 Tipos de datos básicos del C La mayoría de los programas realizan algo útil y generalmente para ello es necesario trabajar con grandes cantidades de datos, si queremos realizar un programa que nos calcule un sistema de ecuaciones tenemos que indicar cuales son las ecuaciones para que el programa las resuelva. Por tanto un programa estará constituido por una serie de datos y una serie de sentencias o instrucciones que le dicen lo que tiene que hacer con esos datos. Los lenguajes de programación disponen de una serie de tipos de datos básicos, y proporcionan herramientas para crear estructuras a medida que faciliten el acceso a la información. Así en nuestro caso ficticio de resolver un sistema de ecuaciones podemos almacenar los coeficientes de cada ecuación con lo que utilizaríamos como tipo de dato los números, si planteásemos el problema desde un punto de vista matricial nos interesaría tener un tipo de datos matriz y lo ideal sería tener un tipo de datos ecuación. En este apartado describiremos los tipos básicos que proporciona el lenguaje C y dejaremos para temas posteriores la declaración de tipos complejos. Estos tipos básicos son los siguientes: +--------+---------------------------------------+ | int | Representa números enteros. | | float | Representa números decimales. | | double | Representa números decimales de mayor | | | precisión. | | char | Representa caracteres. | +--------+---------------------------------------+ Aunque el tipo char represente caracteres internamente para el computador no es más que un número comprendido entre 0 y 255 que identifica un caracter dentro de el código especificado para tal propósito en el sistema en el que nos encontremos trabajando. El código más utilizado para este tipo de representación es el ASCII ya mencionado anteriormente. NOTA: Según la máquina, el compilador empleado y las opciones de compilación activas, "char" puede interpretarse con signo o sin signo. Esto es, de -128 a 127 o desde 0 a 255. Si se requiere una representación que no dependa de las opciones del compilador, etc., se puede poner "signed char" o "unsigned char", según el caso. Como decíamos antes el ordenador debe de disponer de los datos necesarios para resolver el problema para el que lo queramos programar. Difícilmente se podría resolver un sistema de ecuaciones si no se dispone de éstas. Para ello podemos definir variables. Las variables almacenan valores de un tipo especificado y en ellas almacenamos los datos de nuestro problema, se denominan variables por que su valor puede cambiar a lo largo del programa. Para referenciar una variable especificada es necesario que la podamos identificar para ello se utiliza un nombre o identificador que no es más que una secuencia de caracteres, esta secuencia no puede contener caracteres españoles (acentos y eñes), ni caracteres que tengan alguna función especial en el C como por ejemplo los caracteres que representan operaciones matemáticas +, -, etc..., tampoco pueden contener espacios por lo que se suele utilizar el carácter subrayado (_) si el identificador que deseamos asignarle a nuestra variable está formado por varias palabras, pero en general con los caracteres y números tenemos suficiente para dar nombres autoexplicativos, aunque los números no pueden comenzar el nombre de una variable. Las variables se suelen escribir con letra minúscula aunque no es necesario y es aconsejable que los nombres sean auto explicativos para que resulte más sencillo leer el programa (es mejor llamar resultado a una variable que x). Las variables se declaran indicando el tipo que van a tener seguido de su identificador y terminando la línea con un punto y coma. Algunos ejemplos: int numero; /* número no sería un nombre válido */ float mi_variable; char letra; Si necesitamos declarar varias variables de un mismo tipo se pueden incluir en la misma línea todos los nombres que deseemos separándolos por una coma: int numero1,numero2,numero3; float coordenada_x,coordenada_y; El compilador tiene que conocer las variables que va ha utilizar cada bloque para reservarles sitio, por ello las variables se suelen declarar al principio de cada bloque. Puesto que aún no sabemos como definir funciones nuestro programa sólo dispone de un bloque (el main()) con lo que nuestras variables deben de declararse al comienzo del main() dentro del bloque, es decir, inmediatamente a continuación de la llave abierta ({). Un ejemplo: NOTA: Aunque el párrafo anterior da a entender que se puede declarar una variable en cualquier momento, el estándar ANSI C obliga a realizar las declaraciones al principio de cada bloque. En el caso se variables globales la sintaxis es más flexible, para poder utilizar el "Scope" en nuestro provecho. main() { int numero; numero =20; } Podemos también declarar variables fuera del bloque main(). Estas variables se conocen como variables globales y cualquier función puede acceder a ellas, como sólo tenemos una función (main) en este caso nos daría igual declarar las variables dentro o fuera de main. De poco nos servirían estos datos numéricos si no pudiésemos realizar operaciones con ellos, el lenguaje C permite realizar las operaciones básicas con estas variables de tipo numérico, estas son: +---+---------------------------------------------+ | + | para indicar suma | | - | para indicar resta | | * | para indicar producto | | / | para indicar división | | % | para indicar módulo (resto división entera) | +---+---------------------------------------------+ Podemos combinar estas operaciones en la forma que nos plazca con variables o constantes (podemos operar variables con números fijos) y utilizar los paréntesis, caracteres ( y ) para indicar precedencia de las operaciones como lo haríamos en una expresión matemática normal. En principio sólo podemos realizar operaciones con variables que sean del mismo tipo, aunque en general podemos operar los tipos float y double con tipos int o incluso char, en principio no podríamos almacenar un valor float (un número real) en una variable int (entera), para ello tendríamos que convertir ese número real en entero de alguna forma. Podemos convertir tipos básicos utilizando la facilidad del C conocida como cast. Esta facilidad simplemente consiste en indicar antes de una variable o constante el tipo al que lo deseamos convertir entre paréntesis y el compilador se encargará del resto. Un ejemplo : NOTA: El C también define la conversión automática de tipos. float a; int b; b=30; a=(float)b; Para ejemplificar todo esto vamos a realizar un programa que nos calcule el espacio recorrido por un móvil con velocidad uniforme durante un tiempo determinado. El programa sería algo así: #include <stdio.h> main() { float e,v,t; v = 30; /* Velocidad del móvil en Km/h */ t = 5; /* Tiempo durante el cual se mueve */ e = v*t; printf ("\nVelocidad : %f\nTiempo : %f",v,t); printf ("\nEspacio recorrido : %f",e); } Este programa calcula el espacio recorrido por un móvil en movimiento uniforme a una velocidad indicada por la variable v, durante un tiempo indicado por la variable t. Lo más interesante de este programa es que hemos utilizado la función printf para visualizar valores de variables. Como decíamos más arriba la función printf permite visualizar mensajes formateados, es decir, en la cadena de caracteres entre comillas dentro del paréntesis nos indica la forma en la que se visualizarán los datos. Ya hablamos de los caracteres especiales como \n, ahora nos ocuparemos de la visualización de las variables. Cuando deseamos visualizar una variable debemos indicarlo en la cadena de formateo (la secuencia de caracteres entre comillas) mediante el caracter % seguido de un caracter que nos indica el tipo de dato a visualizar. Estos tipos son los siguiente: +-----+------------------------------------------------+ | d,i | Entero en notación decimal con signo. | | o | Entero notación octal sin signo. | | x,X | Entero en notación hexadecimal sin signo. | | u | Entero en notación decimal sin signo. | | c | Entero como caracter simple. | | s | Cadena de caracteres. | | f | Tipo double (ó float) de la forma [-]mmmm.ddd. | | e,E | Tipo double (ó float) en notación científica o | | | exponencial [-]m.dddde+-xx ó [-]m.ddddE+-xx. | | p | Tipo puntero. | +-----+------------------------------------------------+ Podemos así mismo indicar el número de cifras a visualizar, intercalando entre el % y la letra que identifica el tipo de dato un número. Si indicamos dos números separados por un punto, el primer número indica el número total de caracteres a visualizar y el segundo el número de cifras decimales que queremos que se muestren, obviamente este formato sólo se utilizará con tipo float o double. Algunos ejemplos: printf ("%f",numero); Visualiza un número real en el formato normal, parte entera y parte decimal separadas por un punto. printf ("%5.2f",numero); Visualiza un número entero en el mismo formato que la anterior, pero sólo visualizando 5 cifras y siendo dos de ellas reservadas para la parte decimal. Hasta ahora hemos visto como decir a la función printf el formato en el que debe visualizar los datos, pero no le hemos dicho que datos debe visualizar. Lo que se escribirá en el lugar indicado por el % está especificado a continuación de la cadena de formateo entre comillas, separando cada una de ellas por comas. El primer % coincide con el primer parámetro después de las comillas, el segundo con el segundo y así sucesivamente. De esta forma un programa como este: #include <stdio.h> main() { int i; float a,b; i = 10; a = 30.456; b = 678.12; printf ("\nvar1:%d var2:%6.2f var3:%6.1f",i,a,b); } tendrá como salida: var1:10 var2:30.46 var3:678,1 Como se puede observar en el ejemplo si la precisión especificada en la cadena de formateo es menor que la real del número, la función printf aproxima a la precisión especificada. 1.3 Entrada de datos por teclado El programa anterior para el cálculo de el espacio funciona correctamente, pero cada vez que deseemos calcular un nuevo espacio debemos dar valores a las variables v y t y recompilar nuestro programa. Sería estupendo poder leer por teclado los datos de la velocidad y del tiempo y así permitir a nuestro programa trabajar con cualquier valor de velocidad y tiempo. Aquí es donde realmente se comprende de donde viene el nombre de variables. Para leer los datos por teclado se utiliza la función scanf cuya definición se encuentra también en el fichero stdio.h. El formato es idéntico al de printf utilizando una cadena de formateo con los caracteres % y la letra que indica el tipo, ahora del dato que vamos a leer, seguido de la variable en la que deseamos que se lea antecedida por el carácter &. Nuestro programa del móvil se convierte ahora en: #include <stdio.h> main() { float v,t,e; printf ("\nDime la velocidad de el móvil:"); scanf ("%f",&v); printf ("\Dime el tiempo :"); scanf ("%f",&t); e = v*t; printf ("\nUn móvil desplazándose a %5.2f Km/h durante %5.2f horas, recorre una distancia de %5.2f Km",v,t,e); } Ahora cada vez que el programa se ejecute nos pedirá que introduzcamos los valores de la velocidad y el tiempo y nos proporcionará el espacio que recorreríamos, evitando de esta forma la necesidad de recompilar el programa cada vez que deseemos realizar un nuevo cálculo.