Autor: José C. Riquelme (Universidad de Sevilla) TEMA 2. Primeros pasos en C 1. Sintaxis básica. De los seudocódigos del tema 1 se puede intuir que un programa en cualquier lenguaje está constituido por un conjunto de “palabras” (entendida como un conjunto de caracteres dígitos o alfabéticos) separadas por algunos caracteres no alfabéticos. Por ejemplo, si repetimos el seudocódigo del cálculo del factorial de un número: Inicio Leer n Hacer i igual a 1 Hacer f igual a 1 Mientras i sea menor o igual a n Hacer f igual a f × i Hacer i igual a i + 1 FinMientras Escribir el factorial de n es f Fin Podemos ver que hay palabras como i, x o f que representan datos del programa y son palabras que define el programador. Hay caracteres como + o × que representan operaciones, otros como 1 representan valores numéricos y otras palabras como Mientras, Leer o Escribir serán instrucciones del lenguaje. Cuando escribimos en un lenguaje concreto sucede igual. Así existen seis clases de elementos sintácticos en el vocabulario de un lenguaje de programación como el C: separadores, delimitadores, comentarios, palabras clave, identificadores, constantes y operadores. Separadores. En el código de un programa fuente C los separadores –uno o varios espacios en blanco, tabuladores y caracteres de nueva línea– se emplean para separar los demás elementos sintácticos. Los separadores son ignorados por el compilador y su misión es hace el código más legible. El compilador procesa el código fuente constituido entonces por una lista del resto de elementos sintácticos. Si el orden de esta lista es adecuado, no habrá errores de compilación y se creará el código objeto. Delimitadores. Los delimitadores son caracteres especiales que sirven para separar trozos de código o distintos elementos en una lista. En C los principales delimitadores son el punto y coma, las llaves { }, los paréntesis ( ) y las comas. También se usan en determinadas partes del programa o sentencias otros como :, #, <, >, , etc. Comentarios. Los comentarios son un tipo especial de separadores que sirven para explicar o aclarar algunas sentencias del código por parte del programador y ayudar a su prueba y mantenimiento. De esta forma se intenta que el código pueda ser entendido por una persona diferente o por el propio programador algún tiempo después. Los caracteres /* y */ se emplean para iniciar y terminar respectivamente un comentario introducido en el código del programa. Todo el texto entre estos dos caracteres es ignorado por el compilador: variable_1 = variable_2; /* En esta línea se asigna a variable_1 el valor contenido en variable_2 */ Tema 2. Primeros pasos en C El lenguaje ANSI C permite también otro tipo de comentarios. Todo lo que va en cualquier línea del código detrás de la doble barra (//) y hasta el final de la línea, se considera como un comentario. Para comentarios cortos, esta forma es más cómoda que la anterior, pues no hay que preocuparse de cerrar el comentario (el fin de línea actúa como cierre). Como contrapartida, si un comentario ocupa varias líneas hay que repetir la doble barra (//) en cada una de las líneas. Con este segundo procedimiento de introducir comentarios, el último ejemplo podría ponerse en la forma: variable_1 = variable_2; // En esta línea se asigna a // variable_1 el valor // contenido en variable_2 Palabras claves. Las palabras claves o reservadas son las propias del lenguaje e indican tipos de datos o instrucciones concretas. El lenguaje ANSI C tiene pocas palabras claves aunque cada compilador de C añade algunas palabras reservadas. Por supuesto, no deben usarse como identificadores por parte del programador. Las palabras reservadas del ANSI C son las siguientes: auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Identificadores. Un identificador es un nombre que el programador da a una función, un tipo de dato o al contenido de una zona de la memoria (variable) o constante. En ANSI C un identificador se forma con las siguientes reglas: 1. Es una secuencia de letras (minúsculas de la a a la z; mayúsculas de la A a la Z; y dígitos del 0 al 9) o el carácter subrayado o guión bajo (_). No debe incluirse la ñ. 2. Un identificador no puede contener espacios en blanco, ni otros caracteres distintos de los citados, como por ejemplo (*,;.:-+,etc.). 3. El primer carácter de un identificador debe ser siempre una letra o un (_), es decir, no puede ser un dígito. 4. Se hace distinción entre letras mayúsculas y minúsculas. Así, Suma es considerado como un identificador distinto de suma y de SUMA. Ejemplos de identificadores válidos son los siguientes: tiempo, distancia1, caso_A, PI, velocidad_de_la_luz Por el contrario, los siguientes nombres no son válidos (¿Por qué?) 1_valor, tiempo-total, dolares$, %final En general es muy aconsejable elegir los nombres de los identificadores de forma que permitan conocer a simple vista qué representan, utilizando para ello tantos caracteres Autor: José C. Riquelme (Universidad de Sevilla) como sean necesarios. Esto simplifica enormemente la tarea de programación y –sobre todo– de corrección y mantenimiento de los programas. Es cierto que los nombres largos son más laboriosos de teclear, pero en general resulta rentable tomarse esa pequeña molestia. Unas reglas aconsejables para los identificadores son las siguientes: 1. Las variables normalmente tendrán nombres de sustantivos se escribirán con minúsculas salvo cuando estén formadas por dos o más palabras, en cuyo caso, el primer carácter de cada palabra se escribirá en mayúscula. Por ejemplo: salario, salarioBase, edadJubilacion 2. Los identificadores de constantes (datos que no van a cambiar durante la ejecución del programa) se deben escribir con todos los caracteres con mayúsculas. Por ejemplo: PI, PRIMER_VALOR, EDADMINIMA. 3. Los identificadores de funciones normalmente deben indicar alguna acción y se escriben normalmente en minúsculas: calcularFactorial, resolverEcuacion, etc. 4. Los identificadores de tipos de datos definidos por el programador se deben escribir con el primer carácter en mayúsculas: TablaEnteros, TipoEmpleado, etc. Constantes. Una constante (no confundir con identificador de una constante) es una “palabra” que representa un único valor. En C existen distintos tipos de constantes: 1. Constantes numéricas. Son valores numéricos, enteros o reales (también llamados de punto flotante) 1:. Por ejemplo: 123, 34, -456, 1.23, -23.45E3, … 2. Constantes carácter. Cualquier carácter individual encerrado entre simples comillas o apóstrofes (por ejemplo: 'a', 'Y', ')', '+', etc.) es considerado por C como una constante carácter. En realidad internamente C los representa como un número entero (entre 0 y 255), llamado código ASCII, que establece una equivalencia entre cada carácter y el valor numérico correspondiente. 3. Cadenas de caracteres. Un conjunto de caracteres alfanuméricos encerrados entre comillas es también un tipo de constante del lenguaje C, como por ejemplo: "espacio", "Esto es una cadena de caracteres", etc. Operadores. Los operadores son signos especiales –a veces, conjuntos de dos caracteres– que indican determinadas operaciones a realizar con las variables y/o constantes sobre las que actúan en el programa. El lenguaje C es particularmente rico en distintos tipos de operadores: aritméticos (+, -, *, /, %), de asignación (=, +=, -=, *=, /=), relacionales (==, <, >, <=, >=,!=) y lógicos (&&, ||, !). Por ejemplo, en la sentencia: espacio = espacio_inicial + 0.5 * aceleracion * tiempo * tiempo; aparece un operador de asignación (=) y dos operadores aritméticos (+ y *). Otros símbolos como el punto, & ó también son operadores. 2. Tipos de datos básicos y variables. 2.1 Tipos básicos. El C, como cualquier otro lenguaje de programación, tiene posibilidad de trabajar con datos de distinta naturaleza: texto formado por caracteres alfanuméricos, números enteros, números reales con parte entera y parte fraccionaria, etc. Además, algunos de estos tipos de datos admiten distintos rango y/o precisión, 1 También es posible definir constantes en octal o hexadecimal Tema 2. Primeros pasos en C posibilidad de ser sólo positivos o de ser positivos y negativos, etc. Los tipos de datos básicos del C son2: Para datos enteros: int y long. Para datos reales: float y double. Para caracteres: char. Un tipo de dato sirve para definir las variables de un programa. Según los valores que vaya a contener la variable se deberá definir de un tipo u otro. En C es imprescindible definir el tipo de una variable antes de usarla. Una variable no declarada produce un mensaje de error en la compilación. Cuando una variable es declarada se le reserva memoria de acuerdo con el tipo incluido en la declaración. Así por ejemplo si necesitamos una variable para almacenar el año de nacimiento de una persona, la definiremos de tipo int o long por ser un año un valor entero. La diferencia entre los tipos int y long es el tamaño máximo que puede tomar derivado del tamaño que define ese tipo en memoria. Así el tipo int sirve para enteros hasta 32767 (215-1 (¿averigua por qué?) y long para números enteros más grandes. Por ejemplo para almacenar el número de habitantes de una ciudad lo habitual sería definir la variable como long (¿cuál es el tamaño máximo de una variable long?). Para variables reales, por ejemplo un precio en euros o la estatura de una persona se declararán de tipo float o double. La diferencia es de nuevo el tamaño en bytes que ocupa una variable de un tipo u otro en memoria lo que se traduce en tamaño máximo posible y en precisión (número de cifras decimales). La mayoría de los programas trabajan bien con tipos float, que será por tanto nuestro tipo de dato real habitual. (Averigua la diferencia entre el tamaño máximo y la precisión de variables float o double). El tipo char sirve para declarar variables que almacenan un único carácter. Como se ha señalado en el apartado anterior de constantes, C tiene una manera peculiar de almacenar y tratar los caracteres como números enteros. Esto significa que a cada carácter se le asigna un código numérico. El código habitual es el ASCII (American Standard Code for Information Interchange). De esta forma, un dato de tipo char se almacena realmente como un valor entero entre 0 y 255, representando el código ASCII asociado al carácter. Por ejemplo, 'a': 97 'b': 98 'c': 99 ... 'z': 122 'A': 65 'B': 66 'C': 67 ... 'Z': 90 '0': 48 '1': 49 '2': 50 ... '9': 57 Obsérvese que no hay una relación especial entre el valor del carácter constante que representa un dígito y el valor entero intrínseco del dígito; es decir, el valor de '3' no es 3. El hecho de que los valores de 'a', 'b', 'c', etc. sean correlativos es importante, pues simplifica la clasificación de caracteres y palabras en orden alfabético. 2 También existe el tipo short y variantes con las palabras long, signed y unsigned que no se usarán en esta asignatura Autor: José C. Riquelme (Universidad de Sevilla) Algunos caracteres no son imprimibles, y se representan de una forma especia, como por ejemplo '\n' (nueva línea o salto de línea) o '\0' (carácter nulo). Esta forma de representar los caracteres da lugar a cuestiones curiosas y confusiones como que se puedan restar variables de tipo carácter o sumar un entero a una variable char3. (¿Qué resultado darían esas operaciones?). El ANSI C no tiene definido un tipo para las expresiones booleanas 4. Un valor booleano es un valor de cierto o falso, y que en C se modela mediante un valor entero de 1 para cierto y de 0 para falso. Más adelante veremos como se puede definir un tipo lógico. Finalmente C proporciona un tipo denominado void, que representa la ausencia de tipo. 2.2 Variables. Las variables sirven para almacenar datos y valores que pueden cambiar a lo largo de la ejecución. Variables típicas en los programas son aquellas que sirven para guardar el valor de los datos leídos desde teclado, la variable que acumula una suma o un contador, etc. Para declarar variables en C se debe primero escribir el tipo y después la lista de identificadores de las variables que se quieran definir de ese tipo separados por comas y terminados en un punto y coma. Todas las variables deben declararse antes de ser utilizadas y en general, las variables en C no se inicializan a ningún valor, por tanto, es conveniente y muy recomendable inicializarlas cuando se declaran. La declaración se usa para asociar un tipo de dato a un identificador. El tipo de dato representa el intervalo de valores (dominio) que puede tomar la variable y el conjunto de operaciones definidas sobre la misma. Así pues, la información elemental de una variable se compone de: · · · Un tipo, que define un conjunto de valores posibles del dominio donde puede tomar valor la variable. Un valor, que es el elemento del dominio especificado por el tipo que tiene la variable en un momento dado. Un nombre o identificador. Serían declaraciones válidas: int contador=0,m; long numhab; float sumaTotal=0.0, precio=9.99; Las declaraciones de variables en C no pueden hacerse en cualquier parte del código y en función del lugar donde se declaren se establece un determinado ámbito de uso. Esta cuestión se estudiará con profundidad más adelante. 2.3 Conversiones de tipo. Cuando en una expresión se mezclan variables de distintos tipos, por ejemplo, si se suman dos variables una int y otra float, ¿Cuál es el tipo del resultado final? En C las operaciones se realizan en el tipo de mayor rango, es decir, en 3 En esta asignatura no se tendrá en cuenta esta característica y el tipo char sólo se usará para operaciones propias de caracteres. 4 En algunos compiladores existe el tipo bool heredado del C++. Tema 2. Primeros pasos en C este caso la variable int se convierte a float antes de realizar la operación. Esta conversión es automática e implícita (el programador no necesita intervenir, aunque sí conocer sus reglas). Así pues, cuando dos tipos diferentes de constantes y/o variables aparecen en una misma expresión relacionadas por un operador, el compilador convierte los dos operandos al mismo tipo de acuerdo con los rangos, que de mayor a menor se ordenan del siguiente modo: double > float > long > int > char Otra clase de conversión implícita5: tiene lugar cuando el resultado de una expresión es asignado a una variable, pues dicho resultado se convierte al tipo de la variable. En este caso, ésta puede ser de menor rango que la expresión, por lo que esta conversión puede perder información. Por ejemplo, la secuencia de código: int i; float x=4.8; i = x/2; // i valdrá 2 La conversión entre los tipos int y char viene dada por la característica anteriormente señalada de que C almacena los valores char como un int correspondiente a su código ASCII6. 3. Funciones. Como se ha señalado en el Tema 1 la modularización del código se implementa en C mediante el concepto de función. Una función está asociada con un identificador o nombre, que se utiliza para referirse a ella desde el resto del programa. Toda función en C debe estar declarada mediante su prototipo, definida mediante el código de la función e invocada desde la función principal o desde otra función. 3.1 Declaración de una función. La declaración de una función consiste en definir cuál va a ser su nombre, qué tipos de datos tiene como entrada (argumentos) y que tipo de dato devuelve. En una primera aproximación a las funciones en C vamos a estudiar sólo las funciones que devuelven un único valor de salida. Más adelante estudiaremos las funciones que pueden devolver más de un valor. Para explicar estos conceptos hay que introducir los conceptos de valor de retorno y de argumentos. Toda función bien definida debe recibir uno o varios valores a partir de los cuales calculará un valor de salida. El ejemplo más cercano son las funciones matemáticas como potencia, que puede recibir dos argumentos, uno real (base) y otro entero (exponente) y devuelve un real con el resultado de la base elevada al exponente. Por tanto, los argumentos son base y exponente y el resultado o valor de retorno la potencia. Para declarar una función basta con darle un nombre y declarar los tipos de los argumentos y del valor de retorno de esta manera7: 5 En C se pueden realizar también conversiones explícitas mediante el uso de casting. No será materia de esta asignatura. 6 Como se ha dicho anteriormente en esta asignatura no se usará esa conversión aunque el alumno interesado puede busca fácilmente información sobre esta característica (obsoleta) del C. 7 También se puede incluir en el prototipo el nombre de la variable argumento, nosotros sin embargo no lo haremos. Autor: José C. Riquelme (Universidad de Sevilla) Tipo_retorno nombre_funcion (tipo_arg1, tipo_arg2,…,tipo argn); Esto es lo que se llama prototipo de una función y es equivalente a su declaración. Por ejemplo: float potencia (float,int); int factorial(int); char aMayuscula(char); int mcd(int,int); Nótese que un prototipo siempre termina en el separador ; y que los tipos de los argumentos están entre paréntesis y separados por comas. 3.2 Llamada a una función. Las funciones son llamadas o invocadas desde otras funciones. La sintaxis de una invocación típica de una función en C es: variable=nombre_funcion (val_arg1, val_arg2,… ,val_argn); donde variable ha de ser (con algunos matices que veremos más adelante) del tipo que devuelve la función y los elementos val_argi serán variables o constantes de los tipos de los argumentos en el mismo número y orden. Así serían posibles llamadas: pot pot f = c = p = = potencia (3.4,2); // pot de tipo float = potencia(x,n); // x debe ser float y n int factorial(m); // m de tipo int aMayuscula(‘e’); // c de tipo char mcd(i,j); // p, i y j de tipo int En una invocación el nombre de la función (con la lista de argumentos) es un valor del tipo devuelto y, por tanto, también puede intervenir en cualquier tipo de expresiones de acuerdo con el tipo devuelto. Así también son posibles invocaciones: pot = potencia (3.4,mcd(3,p)); // pot de tipo float y p int f = (3*a)/factorial(m); // a, m de tipo int p = mcd(i,j)+mcd(a,b); // p, i, j, a, b de tipo int printf(“el valor es %c”,aMayuscula(g)); con g de tipo char Los valores que figuran en la invocación se llaman argumentos reales o actuales. 3.3 Definición de una función. La definición de una función es el conjunto de sentencias o instrucciones necesarias para que la función pueda realizar su tarea cuando sea llamada. En otras palabras, la definición es el código correspondiente a la función. Además del código, la definición de la función debe incluir una primera línea que es una cabecera similar a la declaración o prototipo que hemos visto. Esta cabecera debe incluir de forma obligatoria los nombres de las variables que van a ser los argumentos de entrada. Estas variables se llaman argumentos formales a diferencia de los argumentos que están en la invocación que ya hemos señalado se llaman argumentos reales. Veamos un ejemplo con la función potencia: Declaración o prototipo: float potencia (float, int); Tema 2. Primeros pasos en C Cabecera de la definición: float potencia (float base, int exponente) Las variables base y exponente son los argumentos formales, y a su vez son variables locales en el código de la función. Aunque no entendamos todavía el código, vamos a exponer cuál sería la definición completa de la función potencia. float potencia (float base, int exponente){ float resultado=1.0; int i=1; while(i<=exponente){ resultado = base * resultado; i=i+1; } return resultado; } Sin tener en cuenta de momento qué hace esta función, sí podemos entender qué significan los parámetros formales. Las variables base y exponente sirven de comunicación entre la invocación y el código de la función, de esta manera la invocación: pot = potencia (3.4,2); hace que la variable base tome el valor 3.4 y exponente el valor 2, que son denominados parámetros reales. Si la invocación se hiciera en vez de con constantes con valores de variables, por ejemplo: pot = potencia (a,b); // a de tipo float y b int Los parámetros reales serían a y b y cuando se invoca la función potencia el valor de a se transfiere a base y el de b a exponente. La función potencia realiza una serie de cálculos con los valores recibidos de base y exponente y calcula un valor que se guarda en la variable local resultado. Esta variable es “devuelta” en la última sentencia de la función y su valor se transfiere a la invocación de la función y asignada a su vez a la variable pot. Es importante repetir que los parámetros formales en la cabecera de la función son variables locales a la función, es decir, sólo tienen sentido dentro del código de la función, y es un error muy común querer definirlos otra vez dentro de la función. Las variables i y resultado también son variables locales de la función potencia. Si una función no devuelva ningún valor su tipo de retorno será void8. 3.4 La función main. Todo programa en C debe tener una función principal que debe denominarse main. Aunque puede tener distintos formatos de cabecera lo usual es void 8 Por razones históricas estas funciones suelen llamarse procedimientos. Por ejemplo, se usan habitualmente para imprimir resultados en pantalla, ya que son funciones que estructuran bien el código y son reutilizables pero no devuelven ningún valor. Autor: José C. Riquelme (Universidad de Sevilla) main(void)9. A continuación entre llaves estará el código que resuelve el problema para el que se crea el programa. Lo usual es que la mayoría del código de la función main sea invocaciones a las funciones en las que se habrá dividido el problema. 3.5 Ámbito de las variables. Como hemos visto en C las variables se declaran en dos sitios: después de la cabecera de la función principal10 y después de las cabeceras de las funciones11. Estas variables tienen como ámbito de visibilidad sólo la función en la que se han declarado. Por tanto, distintas funciones (incluyendo la principal) pueden tener el mismo identificador de variable sin problemas ya que se estarán refiriendo a zonas de memoria diferente. 4. Estructura de un programa en C La estructura de un programa en C no es única, hay cierta libertad, sin embargo en esta asignatura vamos a dar una única estructura posible. El código se puede (y más adelante será obligatorio) estructurar en más de un fichero. De momento, vamos a dar la estructura de un programa en C en un único fichero fuente, que recordamos tendrá la extensión .c. El orden de los elementos adecuado es: 1. 2. 3. 4. 5. 6. declaración de importaciones declaración de constantes declaración de tipos declaración de funciones función principal definición de funciones 4.1 Declaración de importaciones. En C es habitual usar un conjunto de funciones ya predefinidas por el compilador y situadas en librerias. Estas funciones están declaradas en ficheros que tienen la extensión .h (de head o ficheros cabecera). En la declaración de importaciones el programador indica al compilador que librerías va a necesitar. Hay numerosas librerías de funciones y además cada IDE puede añadir funciones propias, sin embargo tres son habituales: stdio.h, math.h y string.h. La librería stdio.h (standard input/output) es la que sirve para las funciones de lectura tanto desde teclado como desde fichero y de escritura tanto en pantalla como en fichero, y por tanto, siempre está presente en un programa en C. La librería math.h contiene las funciones matemáticas (raíz cuadrada, potencia, trigonométricas, etc.) y string.h las funciones para tratamiento de cadenas de caracteres. Cada declaración de importación debe ir en una sola línea con un formato como el siguiente: 9 El C permite que la función principal tenga argumentos de entrada, que pueden ser dados como entrada al programa ejecutable si éste es invocado desde la línea de comando. No será materia de este curso. 10 Realmente el C permite también declarar variables “globales” (fuera del main y de las funciones) y que tendrían visibilidad en todo el ámbito del programa. Se consideran un muy mal hábito de programación y por tanto no se considerarán en este manual. 11 Dependiendo del compilador de C ó C++ que se use, también es posible declarar variables en ámbitos más reducidos, por ejemplo dentro de un bucle. En este manual no usaremos esta posibilidad. Tema 2. Primeros pasos en C #include <stdio.h> #include <math.h> 4.2 Declaración de constantes. Una constante es un identificador que se asocia a un valor que no cambia durante toda la ejecución del programa. Habitualmente se usa para una mejor documentación del programa o para hacer más fácil su mantenimiento y cambio. La forma habitual de declarar una constante es la siguiente: #define identificador valor Por ejemplo #define PI 3.1415 #define NUMMAXIMO 100 #define ERROR “Terminación anormal del programa” Nótese que entre el identificador y el valor NO HAY carácter =, que la sentencia NO termina en punto y coma y que la constante no necesita declararse de un determinado tipo, sino que el valor determina el tipo12. 4.3 Declaración de tipos. El C proporciona un conjunto de tipos básicos ya definidos (int, float, char, etc.) y también permite que el programador defina sus propios tipos. Los tipos más habituales que se definen en C son los tipos enumerados, los tipos tabla o array y los tipos registros o struct. Los estudiaremos más adelante. 4.4 Declaración de funciones. Como se ha señalado en la sección 3.1 las funciones en C deben declararse mediante su prototipo. Éste debe escribirse antes de su invocación por el programa principal. 4.5 Función principal. La función main es por donde empieza a ejecutarse todo programa en C. El código de la función principal consiste habitualmente en la definición de variables locales, la lectura (por teclado o de ficheros) de una serie de datos de entrada que se almacenarán en las variables definidas. Estas variables serán parámetros reales o argumentos de entrada para la invocación de las funciones que resuelven el problema. Finalmente el programa suele terminar mediante la escritura (en pantalla o en fichero) de los resultados obtenidos. El código irá encerrado entre los caracteres { } que delimitan su ámbito. 4.6 Definición de funciones. Finalmente el fichero fuente en C termina con la definición de las funciones que se usan en el programa, bien invocadas desde el main o bien desde otra función. Las funciones se definen una a continuación de otra. Cada definición como se ha señalado en la sección 3.3 de este tema, tiene su cabecera con el tipo que devuelve, nombre de la función, y entre paréntesis y separados por comas los tipos y nombres argumentos formales. Después entre llaves va el código en C de la función, que posiblemente defina algunas variables locales a la función (los argumentos 12 También se pueden definir constantes anteponiendo la palabra const delante de la declaración e inicialización de una variable: const int VALMAX=100; Nótese que este tipo de constantes sí llevan declaración de tipo, signo = y punto y coma al final. Autor: José C. Riquelme (Universidad de Sevilla) ya están definidos en la cabecera). Si la función devuelve un valor de retorno, entonces la definición termina con un return13. 4.7 Organización del código. Cuando un programa en C tiene pocas funciones (tres o cuatro) suele ponerse todo el código en un solo fichero .c. Sin embargo, cuando el programa se hace más complejo, suele organizarse el código en varios ficheros. La organización mínima es un fichero de cabecera o .h que normalmente tiene todas las declaraciones de constantes, tipo y funciones. Un fichero .c con únicamente el programa principal y otro fichero .c con la definición o código de las funciones. Dependiendo de la complejidad del problema y de las relaciones entre funciones, se puede organizar el código con varios ficheros .h y varios .c con las funciones agrupadas por su funcionalidad. Los ficheros .h deben ser incluidos en los ficheros .c mediante una declaración de importación similar a la definida en la sección 4.1 pero en vez de poner el nombre de la librería .h entre < > se indicaría el nombre del fichero cabecera entre “ ” (dobles comillas): #include “tipos_hospital.h” 5. Expresiones. Una expresión es la formulación de una operación matemática formada por constantes, variables, operadores y llamadas a funciones. El resultado de una expresión puede ser un valor numérico y entonces estaríamos ante una expresión aritmética o un valor booleano y sería una expresión lógica. 5.1 Operadores. Un operador es un carácter o grupo de caracteres que actúa sobre una, dos o más variables para realizar una determinada operación con un determinado resultado. Ejemplos típicos de operadores son la suma (+), el producto (*), menor o igual (<=), etc. 5.1.1 Operadores Aritméticos. Sirven para operar entre valores numéricos. En C se utilizan los seis operadores siguientes: – Suma: + – Resta: – Multiplicación: * – División: / – Resto: % - Cambio de signo: Todos estos operadores se pueden aplicar a constantes, variables y expresiones. El resultado es el que se obtiene de aplicar la operación correspondiente entre los dos operandos. El único operador que requiere una explicación adicional es el operador resto %. En realidad su nombre completo es resto de la división entera. Este operador 13 Realmente una función puede tener varias sentencias return¸ aunque es una buena práctica de programación que sólo haya un return en cada función. Tema 2. Primeros pasos en C se aplica solamente a constantes, variables o expresiones de tipo int. Por ejemplo, 17%3 es 2, puesto que el resto de dividir 17 por 3 es 2. Por tanto, si a%b es cero, a es múltiplo de b. Un ejemplo de expresión en la que intervienen operadores aritméticos es el siguiente polinomio de grado 2 en la variable x: 5.0 + 3.0*x - x*x/2.0 Las expresiones pueden contener paréntesis (...) que agrupan a algunos de sus términos. Puede haber paréntesis contenidos dentro de otros paréntesis. El significado de los paréntesis coincide con el habitual en las expresiones matemáticas. Es habitual la inclusión de espacios en blanco para mejorar la legibilidad de las expresiones. 5.1.2 Operadores de Asignación. Los operadores de asignación sirven para dar valor a una variable –es decir, asignan a la zona de memoria correspondiente a dicha variable un valor concreto– el resultado de una expresión o el valor de otra variable (en realidad, una variable es un caso particular de una expresión). El operador de asignación más utilizado es el símbolo =14. Su forma general es: nombre_de_variable = expresion; La semántica de esta expresión es la siguiente: en primer lugar se evalúa la expresion, y en segundo lugar se almacena el valor obtenido en la posición de memoria correspondiente a la variable cuyo identificador aparece a la izquierda del símbolo igual (realizándose la correspondiente conversión de tipo si es necesario). Por ejemplo, dadas dos variables x e y, donde x vale 7 e y vale 3 x 7 y 3 después de la asignación y = x + 1; el contenido de ambas variables será x 7 y 8 Como se ve, el efecto de la asignación es destructivo: cuando se asigna un nuevo valor a una variable se pierde el valor anterior de la variable. 14 No debe ser confundido con la igualdad matemática Autor: José C. Riquelme (Universidad de Sevilla) Hay dos errores relacionados y que son típicos en programadores principiantes. Uno es intentar poner a la izquierda del = una expresión en vez de una variable15, y el segundo es no entender bien una asignación cuando intervienen sólo dos variables, por ejemplo x = y; En este caso, el valor que tuviera y se “almacena” en la dirección de memoria de x, haciendo que el anterior valor de x “desaparezca”. Por supuesto, la variable y no sufre ninguna modificación. Una posible utilización de este operador es como sigue: variable = variable + 1; variable = variable - 1; que incrementa o decrementa el valor de variable en uno. Para estas operaciones que son habituales, el C proporciona dos operadores que simplifican este tipo de expresiones escribiendo una única vez el nombre de la variable. Estos operadores son ++ y --. Por ejemplo, las dos expresiones de arriba son equivalentes a variable++; variable--; Estos dos operadores tienen distinto significado según aparezcan antes o después del nombre de la variable y además pueden aparecer dentro de expresiones más complejas. En esta asignatura sólo usaremos los operadores ++ y -- en expresiones simples como las de arriba. Existen otros cuatro operadores de asignación (+=, -=, *= y /=) formados por los cuatro operadores aritméticos seguidos por la asignación. Estos operadores simplifican algunas operaciones recurrentes sobre una misma variable. Su forma general es: variable op= expresion; donde op representa cualquiera de los operadores (+ - * /). La expresión anterior es equivalente a: variable = variable op expresion; Por ejemplo, las dos asignaciones siguientes son equivalentes: suma += valor; suma = suma + valor; 5.1.3 Operadores Relacionales. Los operadores relacionales sirven para comparar dos expresiones aritméticas. Los operadores relacionales de C son los siguientes: – Igual que: == – Menor que: < – Mayor que: > 15 En este caso el compilador nos avisará del error sintáctico Tema 2. Primeros pasos en C – Distinto que: != – Menor o igual que: <= – Mayor o igual que: >= Su forma general de uso es la siguiente: expresion1 op expresion2 donde op es uno de los operadores (==, <, >, <=, >=, !=). El funcionamiento de estos operadores es el siguiente: se evalúan expresion1 y expresion2, y se comparan los valores resultantes. El resultado de la comparación es un valor de falso o cierto, lo que en programación se conoce como booleano. Como se ha dicho anteriormente el ANSI C no tiene un tipo lógico o booleano definido, sino que usa un tipo int para simular los valores de falso (0) o cierto (1) 16. Otro error típico entre programadores de C es usar el operador de asignación = como operador de comparación. 5.1.4 Operadores Lógicos. Los operadores lógicos son operadores que permiten combinar los resultados de los operadores relacionales, comprobando que se cumplen simultáneamente varias condiciones, que se cumple una u otra, etc. El lenguaje C tiene tres operadores lógicos: el operador Y de conjunción lógica (&&), el operador O de disyunción (||) y el operador NO de negación (!) Su forma general de uso es el siguiente: expresion1 || expresion2 expresion1 && expresion2 ! expresion El operador && devuelve un 1 si expresion1 y expresion2 son verdaderas (o distintas de 0), y 0 en caso contrario, es decir si una de las dos expresiones o las dos son falsas (iguales a 0). Por otra parte, el operador || devuelve 1 si al menos una de las expresiones es cierta. Finalmente, el operador ! cambia el valor de expresion de cierto a falso o viceversa. En resumen la tabla de verdad de estas operaciones es la siguiente: a b a || b a && b !a falso falso falso falso cierto falso cierto cierto falso cierto cierto falso cierto falso falso cierto cierto cierto cierto falso Además de los operadores vistos hasta ahora, el lenguaje C dispone de otros operadores que veremos más adelante, en especial los operadores que trabajan con direcciones de memoria. 16 En realidad cualquier valor distinto de 0 es interpretado por el C como cierto. Autor: José C. Riquelme (Universidad de Sevilla) 5.2 Precedencia y asociatividad. El resultado de una expresión depende del orden en que se ejecutan las operaciones. Por ejemplo, la expresión 3 + 4 * 2, si se realiza primero la suma (3+4) y después el producto (7*2), el resultado es 14; si se realiza primero el producto (4*2) y luego la suma (3+8), el resultado es 11. Para saber en qué orden se realizan las operaciones es necesario definir unas reglas de precedencia y asociatividad. La tabla siguiente resume las reglas de precedencia y asociatividad de los principales operadores en el lenguaje C. Los operadores que están en la misma línea tienen la misma precedencia, y las filas están en orden de precedencia decreciente. Operador Asociatividad ! ++ -- - (cambio de signo) derecha a izquierda * / % izquierda a derecha + - (diferencia) izquierda a derecha < <= > >= izquierda a derecha == != izquierda a derecha && izquierda a derecha || izquierda a derecha Por supuesto, el orden de evaluación puede modificarse por medio de paréntesis, pues siempre se realizan primero las operaciones encerradas en los paréntesis más interiores. En general, es recomendable el uso de los paréntesis para remarcar y documentar el orden de las operaciones. 5.3 Funciones matemáticas. Como ya se ha se comentado, en una expresión también pueden intervenir funciones tanto las definidas por el programador como las que proporciona el compilador. Las funciones matemáticas más comunes que proporciona el C incluidas en la librería math.h son: sin (x) cos (x) tan (x) exp (x) log (x) pow (x, y) sqrt (x) fabs (x) seno de x coseno de x tangente de x función exponencial ex logaritmo natural ln(x), x>0 potencia de x elevado a y raíz cuadrada de x valor absoluto de x En todas estas funciones, tanto los valores de x e y como los resultados son de tipo double. Los ángulos para las funciones trigonométricas deben estar expresados en radianes. Tema 2. Primeros pasos en C 6. Entrada y salida No hay palabras reservadas del lenguaje que realicen tareas de lectura y escritura, sino que todas estas operaciones están suministradas por la biblioteca stdio.h, que contiene un conjunto de funciones de entrada/salida. 6.1 Entrada y salida de caracteres17. La función getchar lee un carácter del teclado y devuelve su valor cuando se pulsa la tecla de retorno de carro. La función putchar escribe un carácter en la pantalla. Por ejemplo, siendo ch una variable de tipo char, el siguiente fragmento de código ch = getchar (); putchar (ch); lee un carácter de la entrada estándar y lo asigna a la variable ch. Después escribe el mismo carácter en la salida estándar. 6.2 Entrada y salida con formato. 6.2.1 La función printf. La función printf permite escribir una lista de datos de acuerdo con un formato prestablecido. Acepta diferentes tipos de argumentos: carácter, valor numérico entero o real, o una cadena de caracteres, y los escribe según un formato especificado sobre la salida estándar. La forma de la función printf es printf ("formato", arg1, arg2, ..., argn); donde los argi pueden ser constantes, variables o expresiones en general, y formato es una serie de caracteres en la cual se pueden encontrar dos clases de datos: un mensaje o texto a escribir literalmente, y los símbolos especificadores del formato con el cual se van a escribir las variables dadas como argumentos. Por ejemplo, printf ("Esto es un mensaje\n"); Escribe la cadena Esto es un mensaje seguida de un retorno de línea (debido al '\n'). Los especificadores de formato deben ir precedidos del carácter %. Algunos de ellos son: %c %d %ld %f %lf %s 17 carácter (char) número entero (int) número entero largo (long) número real (float) número real (double) cadena de caracteres Se refiere a la lectura y escritura de variables de tipo char (un único carácter). La lectura y escritura de cadenas de caracteres se explicarán cuando se definan. Autor: José C. Riquelme (Universidad de Sevilla) Por cada argumento a imprimir debe existir un especificador de formato, que indica el formato en el cual se va a mostrar el dato. Por ejemplo, siendo letra una variable de tipo char con el valor 'A', la instrucción printf ("El código ASCII de %c es %d", letra, letra); escribiría El código ASCII de A es 65 mostrándose el mismo dato con dos formatos distintos: como carácter (%c) y como entero (%d). Obsérvese que hay dos especificadores de formato y dos argumentos (aunque en este caso sean el mismo). Otros ejemplos: printf ("\nEl cuadrado de %f vale %f", x, x*x); printf ("\nLa edad de %s es %d años", "Juan", edad); printf ("\nSeno(%lf) = %lf\n", x, sin(x)); La función printf puede emplearse sin argumentos, en cuyo caso sólo se imprimen los caracteres presentes en la cadena de formato. Entre el símbolo % y el carácter que especifica la notación a emplear se pueden insertar ciertos caracteres opcionales. Son los siguientes: El signo menos (-) para que el dato se ajuste por la izquierda, en lugar de hacerlo por la derecha, que es lo establecido a falta de especificación explícita. Un número que indica la longitud mínima en caracteres que tiene el campo donde se imprimirá el dato correspondiente. Los espacios juegan el papel de caracteres de relleno. Un punto decimal (.) seguido de una cifra que indica el número de dígitos después del punto decimal de un dato flotante, o el número mínimo de dígitos para un entero, o el número máximo de caracteres de una cadena que serán impresos. Por ejemplo: %6d imprime un número entero (int) alineado por la derecha y en un campo de al menos seis caracteres. %-10s imprime una serie de caracteres alineada por la izquierda y asegurando una longitud mínima de 10 caracteres. %.4f imprime un número real (float) en coma flotante con un máximo de 4 cifras significativas en la parte fraccionaria. Tema 2. Primeros pasos en C 6.2.2 La función scanf. La función scanf permite leer valores desde la entrada estándar (teclado) y almacenarlos en las variables que se especifican como argumentos. Éstas deben ir normalmente precedidas por el símbolo &18. La sintaxis de scanf es scanf ("formato", arg1, arg2, ..., argn); donde debe haber tantos especificadores en la cadena de formato como variables en la lista de argumentos. Por ejemplo, scanf ("%d%lf", &n, &x); para leer un valor n de tipo int y un valor x de tipo double. Estos valores se introducirán desde la entrada estándar separándolos por espacios o retornos de carro. En la cadena de formato aparecen especificadores de formato, que son los mismos que se han visto para printf pero, a diferencia de lo que ocurre con éste, nunca deben aparecer caracteres que no sean formatos. 6.3 Entrada y salida de C++. En esta subsección se introducen los mecanismos de lectura y escritura del C++. Tienen la ventaja de ser más fáciles de usar que los del ANSI C, ya que no exigen especificar el tipo de la variable mediante un formato y muchos compiladores de C actuales permiten su uso19. Los objetos de flujo de entrada y salida son cin y cout. El primero cin seguido del operador >> y una variable, lee lo que se introduce desde la entrada estándar (teclado) y queda almacenado en dicha variable, por ejemplo: int numero; cin >> numero; El complementario es el operador cout, equivalente a la función printf, pero sin indicarle el tipo de la variable que queremos imprimir, por ejemplo: cout << "Vamos a imprimir un numero entero: "; cout << numero; Para poder utilizar estos comandos debe poner como cabecera del fichero principal las siguientes importaciones: #include <iostream> using namespace std; 18 La razón para este símbolo se estudiará más adelante. El compilador Visual C permite usar esta lectura, para ello el código fuente debe tener la extensión .cpp 19 Autor: José C. Riquelme (Universidad de Sevilla) 7. Ejercicios 1. Indique cuáles de los siguientes identificadores no son válidos y por qué. a) b) c) X_max 5down SumaTotal d) e) f) _total si_o_no contador2 g) h) i) si*no longitud long 2. Defina las siguientes constantes: a) una constante entera EOF con valor -1. b) una constante entera MAXIMO con valor 999. c) dos constantes enteras CIERTO y FALSO con valores 1 y 0, respectivamente. d) una constante carácter ULTIMA_LETRA con valor 'Z'. e) una constante real PI con valor 3.14159. f) una constante real EPSILON con valor 0.0001 (utilice la notación científica). g) una cadena constante MENSAJE con valor "Error en la entrada de datos". 3. Dadas las siguientes definiciones de constantes #define #define #define #define #define MINIMO 1 MAXIMO 5 VALOR1 7 VALOR2 9 VALOR3 -1 y la siguiente declaración de variable int numero; Escriba expresiones lógicas que sean verdaderas si a) El valor de numero se encuentra en el las constantes MINIMO y MAXIMO. b) El valor de numero es uno de los tres VALOR1, VALOR2 o VALOR3. c) El valor de numero se encuentra en el las constantes MINIMO y MAXIMO, o bien constante VALOR1. rango definido por valores constantes rango definido por es igual al valor 4. Dadas las siguientes expresiones matemáticas, escriba las correspondientes expresiones en lenguaje C. a) b) c) d) e) f) Tema 2. Primeros pasos en C a x2+bx + c a + b - b + b2 - 4ac a* b 2a 3 1 1+ a- 1 1+ 1+ 5 a a + 6 120 2 2 sin (x + 1) + cos (x + 1) - 1 1 x 5. Dada la declaración de variables int a=1, b=5, c=2; indique el valor de la siguiente expresión lógica: (((a < b) || (c == 1)) && !(b <= (a - c))) 6. Dadas las variables int i = 5, j = 7, edad = 15; double suma = 12.559; char vocal = 'a'; indique el resultado impreso por las siguientes instrucciones: a) b) c) d) e) f) g) h) printf printf printf printf printf printf printf printf ("El cuadrado de %d vale %d", i, i*i); ("%3d x %3d = %3d", i, j, i*j); ("%3d x %3d = %10.5f", i, j, i*j); ("La suma es: %.2lf", suma); ("La edad de %s es %d años", "Rosario", edad); ("Nombre Edad\n%s%4d", "Rosario", edad); ("Nombre Edad\n%10s%4d", "Rosario", edad); ("Nombre Edad\n%-10s%4d", "Rosario", edad); 7. El siguiente programa calcula el área de un rectángulo cuyos datos se leen de la entrada estándar. ¿Qué errores encuentra en el mismo? include <stdio.h> void main (void) { int base, altura; printf ("\nCálculo del área de un rectángulo"); printf ("\nPor favor, introduzca base y altura: "); /* Leer base y altura */ scanf ("%c%c", &base, &altura); /* Calcular e imprimir área area = base*altura; printf ("\n\nEl área del rectángulo es: %d\n", area); } Autor: José C. Riquelme (Universidad de Sevilla) 8. Escriba un programa para convertir de grados Fahrenheit a Celsius. La fórmula de conversión es la siguiente: 5 C = *( F - 32) 9 9. Escriba un programa que lea un instante de tiempo expresado en horas, minutos y segundos y calcule el número de segundos transcurridos desde las cero horas hasta dicho instante de tiempo. Utilice la fórmula segundos_transcurridos = horas * 3600 + minutos * 60 + segundos 10. En los programas anteriores que necesiten leer desde teclado o imprimir en pantalla, cambie las sentencias scanf y printf por sus equivalentes en C++ con cin y cout.