Introducción a la Programación en C. Informática Industrial (3º I.T.I.) Introducción a la Programación en C. • • • • • • • • • • • Introducción. Tipos de datos, variables y constantes. Tiras de caracteres (string), printf() y scanf(). getchar() , getch() y putchar(); Operadores, expresiones y sentencias. Condicionales. Bucles. Funciones y punteros. El preprocesador C. Arrays , estructuras y uniones. Librerías. 1 Introducción • Creado por Dennis Ritchie en 1972. • Lenguaje Compilado (frente a interprete). – Creación del programa ⇒ Código fuente. – Compilación del programa ⇒ Código objeto. – Enlazado del programa con funciones de librería utilizadas ⇒ Fichero ejecutable. • Lenguaje de Nivel Medio (frente a alto y bajo nivel). • Lenguaje Estructurado (frente a no estructurado). Introducción • Estructura general de un programa escrito en C. Declaraciones globales tipo_devuelto main (lista de parámetros) { Secuencia de sentencias } tipo_devuelto funcion_1 (lista de parámetros) { Secuencia de sentencias } ...... tipo_devuelto funcion_n (lista de parámetros) { Secuencia de sentencias } 2 Introducción • Ejemplo1: /*Fichero Incluido*/ #include <stdio.h> /*Variable Global*/ int numero = 1; /*Prototipo de la Funcion*/ void escribe(void); /*Funcion Main*/ void main(void) { /*Variable Local*/ int numero = 0; printf("Esto es una variable local. Numero = %d\n", numero); /*LLamada a la funcion escribe*/ escribe(); } /*Funcion escribe*/ void escribe(void) { printf("Esto es una variable global. Numero = %d\n", numero); } Tipos de datos, variables y constantes. • Cinco tipos de datos básicos: – Carácter (char): 8 bits ⇒ -128 a 127. – Entero (int): 32 bits ⇒ -2.147.483.648 a 2.147.483.647. – Coma flotante (float): 32 bits ⇒ 3.4·10e-38 a 3.4·10e+38. – Coma flotante de doble precisión (double): 64 bits ⇒ 1.7·10e-308 a 1.7·10e+308. – Sin valor (void). • Modificación de los tipos básicos para int y char: – signed: por defecto – unsigned – long: también aplicable a double. 80 bits ⇒ 3.4 x 10e4932 a 1.1·10e+4932 – short 3 Tipos de datos, variables y constantes. • Variable: posición de memoria al que se le asigna un nombre y que se usa para almacenar un valor que puede ser modificado por el programa. • Definición de variables: tipo <lista de variables separadas por coma>; • Ejemplos: – int a; – unsigned char caracter1, caracter2; – double valor1 = 1.2, VALOR1 = 1e-4; Tipos de datos, variables y constantes. • Tipos de Variables: – Variables locales: son las variables definidas dentro de una función o bloque {}. Sólo son conocidas dentro de ese bloque. Tras salir del bloque se destruye. – Variables globales: son aquellas variables que se conocen a lo largo de todo el programa y se pueden usar en cualquier parte del código. • Ejemplos: – ejemplo1.c – ejemplo2.c 4 Tipos de datos, variables y constantes. /* * */ EJEMPLO2.C /*Fichero Incluido*/ #include <stdio.h> /*Variable Global*/ int numero = 1; /*Prototipo de la Funcion*/ void escribe(void); /*Funcion Main*/ void main(void) { /*Variable Local*/ int numero = 0; printf("Esto es una variable local de main. Numero = %d\n", numero); if (1) { int numero = 2; printf("Esto es una variable local del if de main. Numero = %d\n", numero); } printf("Esto es una variable local de main. Numero = %d\n", numero); /*LLamada a la funcion escribe*/ escribe(); } Tipos de datos, variables y constantes. /*Funcion escribe*/ void escribe(void) { printf("Esto es una variable global. Numero = %d\n", numero); if (1) { int numero = 2; printf("Esto es una variable local del if de escribe. Numero = %d\n", numero); } printf("Esto es una variable global. Numero = %d\n", numero); } 5 Tipos de datos, variables y constantes. • Especificadores de clase de almacenamiento: – Variables externas (extern): se utilizan para definir variable globales que deben ser conocidos por los distintos módulos de código que forman un proyecto. – Variables estáticas (static): son variables permanentes dentro de su propia función o archivo. • Variables estáticas locales: Se crea un almacenamiento permanente para ellas, pero su contenido es conocido únicamente dentro de su bloque. • Variables estáticas globales: Se indica de esta forma al compilador que cree una variable global que únicamente puede ser conocido dentro del archivo en el que se ha declarado. – Variables registro (register): Son variables que deben tener un acceso “los más rápido posible” Tipos de datos, variables y constantes. • Constantes: Valores fijos que no pueden ser modificados por el programa. • Definición de las constantes: const tipo nombre_constate = valor; • Ejemplos: – const char a = ‘p’; – const float constante = 2.5; • Constantes hexadecimales: 0xa02. • Constantes octales : 072 • Constantes de cadenas, o tira de caracteres: “hola”. 6 Tipos de datos, variables y constantes. • Constantes de caracteres especiales: \b \f \n \r \t \” \’ \0 \\ \v \a \N \xN Espacio atrás Salto de página Salto de línea Salto de carro Tabulación horizontal Comillas dobles Comilla simple Nulo Barra invertida Tabulación vertical Alerta Constante octal (N es cte. octal) Constante hexadecimal (N es cte. hexadecimal) Tiras de caracteres (string), printf() y scanf(). • Una tira de caracteres es una serie de uno o más caracteres. • No existe ningún tipo especial para ellos. Se almacenan como un array de caracteres. • Ejemplo: – char tira[]=“Esto es un string”; E s t o e s u n s t r i n g \0 – El carácter nulo se utiliza en C para marcar el fin de un string. Dicho carácter no es el cero, sino un carácter no imprimible que le corresponde el codigo ASCII 0. 7 Tiras de caracteres (string), printf() y scanf(). • Printf() y scanf() nos permite comunicarnos con el exterior a traves de la pantalla y el teclado respectivamente (entradas y salidas estandar). • Formato de printf(): printf(“tira de control[Identificadores]”,variables); • Identificadores: %d ⇒ Entero decimal. %c ⇒ Carácter. %s ⇒ String. %e ⇒ Número de coma flotante en formato notación exponencial. – %f ⇒ Número de coma flotante en notación decimal. – – – – Tiras de caracteres (string), printf() y scanf(). • Identificadores (continuación): – – – – %g ⇒ usa %f o %e, el que sea más corto. %u ⇒ Entero decimal sin signo. %o ⇒ Entero octal sin signo. %x ⇒ Entero hexadecimal sin signo. • Ejemplos: 1.- printf(“Lo mas sencillo”); ⇒ Lo mas sencillo. 2.- int x = 3; ⇒ x=3 printf(“ x = %d”,x); 3.- double x = 2.0, y = 3.0; printf(“x = %f, “, x); x = 2.0, y = 0.3e+001 ⇒ x*y = 6.0 printf(“y = %e.\n”, y); printf(“x*y = %f.”, x*y); 8 Tiras de caracteres (string), printf() y scanf(). • scanf() se utiliza para introducir información a través del teclado. • scanf() usa una tira de caracteres de control y una lista de argumentos, donde la tira de caracteres indica los tipos de las variables, y la lista de argumentos son los punteros (direcciones) a dichas variables. • Forma de representar los punteros a una variables: – Si variable es: tipo x; ⇒ su dirección es &x; – Si variable es: tipo x[]; (es decir, es un array) ⇒ su dirección es x. Tiras de caracteres (string), printf() y scanf(). • Las tiras de control son idénticas a las de printf(), con las siguientes excepciones: – No existe la opción %g. – Las opciones %f y %e son equivalentes. – Existe una opción %h para leer enteros short. • Ejemplos: – int dia; float hora; scanf(“%d %f”, &dia, &hora); – char nombre[30]; scanf(“%s”, nombre); • Realizar un programa que calcule la longitud de un circulo y el área de una circunferencia. El radio debe ser una variable global. Los dos calculos deben hacerse en funciones separadas. 9 getchar(), getch() y putchar() • getchar() y putchar() tienen cierta semejanza con scanf() y printf(). El primero toma un carácter de teclado, y el segundo manda un carácter a la pantalla. • getch() toma también un carácter, pero a diferencia del anterior, no produce eco en la pantalla y no es necesario pulsar la tecla ENTER. • Esta última instrucción nos puede servir para hacer una pausa en la ejecución de nuestro programa. Operadores, expresiones y sentencias. • Operadores básicos: – Operador de asignación: = • No es del todo equivalente al igual matemático. A la derecha siempre estará el valor a asignar, mientras a la izquierda tenemos el nombre de la variable ⇒ La asignación se realiza de derecha a izquierda. • Es válido la siguiente expresión: i = i +1; • Son validas multiples asignaciones: a = b = c = 1.0; – Operador de adición: + – Operador de sustracción: – Operador signo: • Sirve, además de para indicar el signo negativo, para cambiar el signo algebraico de un valor. 10 Operadores, expresiones y sentencias. • Operadores básicos (continuación): – Operador de multiplicación: * – Operador de división: / • El resultado de una división de valores enteros da como resultado un entero, por ejemplo, 5/2 = 2. El redondeo que se produce es siempre por defecto. • Precedencia en los operadores básicos: Operador Asociatividad () - (unario) */ + - (sustracción) = Izquierda a Derecha Izquierda a Derecha Izquierda a Derecha Izquierda a Derecha Derecha a Izquierda Operadores, expresiones y sentencias. • Otro operadores adicionales: – Operador módulo: % • Proporciona el resto de la división de dos números enteros. Por ejemplo: 13%5 = 3. • Sólo es valido para números enteros. – Operadores incremento y decremento: ++ y -• Aumenta o disminuye en uno el valor de su operando. • Existen dos variadades: – postincremento (postdecremento): a++ (a--) – preincremento (predecremento): ++a (--a) • El primero es usado antes de cambiar: valor = 2 * a++; • El segundo cambia antes de usar: valor = 2 * ++a; • La precedencia de estos operadores sólo es superada por la de los paréntesis. 11 Operadores, expresiones y sentencias. • La expresiones son las combinaciones de los operandos. • Ejemplos: 4; -6, 4*2, a*(b+c/3)-4/3, a=(3+b++)/c, a = q > 3, 6 + (a = 4 + 3) • En C cada expresión tiene un valor. • Las sentencias son los elementos con los que se construyen los programas. Además, es una instrucción completa para el ordenador y, por lo tanto, completa una acción. • En C una sentencia se representa acabandolas en punto y coma. Por lo tanto, a=3 es una expresión, mientras a=3; es una sentencia. Operadores, expresiones y sentencias. • Una sentencia implica una instrucción, pero una instrucción no tiene porque implicar una sentencia. Por ejemplo: x = 6 + (y = 3 + 2); es una sentencia pero dos instrucciones. • Se denomina sentencia compuesta a un conjunto de dos o más sentencias agrupadas y encerradas entre llaves. Se le denomina también bloque. Se utilizan en condicionales y bucles. 12 Condicionales • Las condicionales nos van a permitir tomar decisiones durante la ejecución de nuestro programa. Dos ordenes: if y switch. • Formato de if: • Ejemplos: if (condicional1) { sentencias; } else if (condicional2) { sentencias; } else { sentencias; } 1.- if (a > 3) x=5; 2.- if (condicion1) { if (condicion2) a = 0; b=1; condicion1 = 0; } else condicion1 = 1; Condicionales • Siempre que la condición sea verdadera, se ejecutará las sentencias que tenga asociada. • Las condicionales son expresiones, y consideraremos que es verdadera siempre que su valor sea distinto de 0. En caso de ser 0 la condicional es falsa. • Para escribir las condicionales usaremos los operadores de relación y los operadores lógicos. • Operadores de relación – – – – – – < ⇒ menor que. <= ⇒ menor o igual que. = = ⇒ igual a. >= ⇒ mayor o igual que. > ⇒ mayor que. != ⇒ distinto de. 13 Condicionales • Los operadores de relación no sirven para comparar tiras de caracteres. • Se pueden usar con números en coma flotante, pero se aconseja exclusivamente > y/o <. • La prioridad de los operadores de relación es menor que el de + y -, pero mayor que el de asignación. Por ejemplo: x = 8 > 3+2 es equivalente a x = 8 > (3+2) y da como resultado x=0, es decir, falso. • Dentro de los propios operadores,los más prioritarios son: <, <=, >= y >. Son menos prioritarios = = y !=. La asociatividad se realiza de izquierda a derecha. Condicionales • Operadores lógicos: – && ⇒ Y – || ⇒ O – ! ⇒ NOT • La prioridad de ! es la misma que la los operadores incremento (++) y decremento (--), sólo superada por los paréntesis. • La prioridad de && es mayor que la de || estando ambos situados por debajo de los operadores de relación. • La asociatividad de todos ellos es de izquierda a derecha. 14 Condicionales • Ejemplo4: /* * EJEMPLO$.c */ #include <stdio.h> #define TRUE 1 #define FALSE 0 void main(void) { int ch; /*introducción de caracteres*/ long nc = 0; /*Número de caracteres*/ int nl = 0; /*Número de líneas*/ int np = 0; /*Número de palabras*/ int palabra = TRUE; /*Número de palabras*/ int terminar = FALSE; /*Condicion de fin de bucle*/ terminar = (ch = getchar()) == '-'; Condicionales while ( !terminar ) { nc++; if (ch == '\n') nl++; if ( ch != ' ' && ch != '\n' && ch != '\t' && palabra == FALSE) { palabra = TRUE; np++; } if ( (ch == ' ' || ch == '\n' || ch == '\t') && palabra == TRUE) palabra = FALSE; terminar = (ch = getchar()) == '-'; } printf("Caracteres = %ld.\nPalabras = %d.\nLineas = %d.\n", nc, np, nl); }/*main*/ 15 Condicionales • En C existe una forma compacta de expresar la sentencia if-else. Es el operador condicional. • Formato del operador condicional ?: – var = expresión1 ? expresión2 : expresión3; – var toma el valor expresión2 si expresión1 es cierta. Si es falsa, entonces var toma el valor de expresión3. – Ejemplo: • x = (a>b) ? c:d; es equivalente a • if (a>b) x = c; else x = d; Condicionales. • La otra instrucción en C para representar una condicional es switch. • Es una forma de simplificar las instrucciones ifelse if-else if -...- else. • Formato de switch: switch (valor){ case constante1: sentencias; break; case constante2: sentencias; break; ... case constanten: sentencias; break; default: sentencias; } 16 Condicionales • Algunas características: – Sólo puede comprobar la igualdad. – No puede haber dos constantes case que tengan los mismos valores. – Se puede utilizar las misma sentencias para distintos case de la siguiente forma: switch (ch){ case ‘a’: case ’b’: case ‘c’: sentencias; break; ... Default: sentencias; } – Está permitido el uso de switch anidados. Condicionales. • Ejemplo5: Realizar un programa que ofrezca unas determinadas opciones al usuario (por ejemplo, realizar distintas operaciones matemáticas) y que en función de la opción solicitada realice dicha tarea. 17 Bucles • Los bucles son estructuras que nos permiten repetir una parte de código las veces que necesitemos. (Por ejemplo: multiplicación de matrices). • Dos tipos: – while y do...while – for • while tiene el siguiente formato: – while (condicion) { sentencias; } Bucles • La condición es una expresión de test, de tal forma que si es verdadera se ejecutará dicho bucle y si es falsa no se ejecutará. Este hecho implica que dicha condición de test se debe modificar durante la ejecución del bloque asignado al bucle. En caso de no ser así nos encontrariamos con un bucle infinito. • Para la condición de test se pueden usar todos los operadores vistos hasta ahora (aritmético, de relación y condicionales). 18 Bucles • do...while tiene el siguiente formato: – do{ sentencias; } while (condición); • La estructura do...while siempre se ejecuta, al menos, una vez, mientras que while puede o no ejecutarse depediendo del valor de la condición. • Excepto esta situación, no existe ninguna diferencia entre ambas estructuras. Bucles • Ejemplo6: /* * */ EJEMPLO5.c #include <stdio.h> #include <conio.h> void main (void) { int datos[] = {1,4,5,7,9,10,22,25,27,30,33,34,35,37,39,40,50,55,60}; /*array de 19 datos de tipo entero ordenados de menor a mayor*/ int entrada; /*Variables para definir numero a buscar*/ int maximo = 18,minimo = 0; /*Rango de busqueda*/ int posicion; /*variable utilizada para definir la posicion de busqueda*/ int k = 0; /*variable utilizada para mostrar en pantalla los posibles valores a buscar y para definir el numero de pasos utilizado para buscar la entrada*/ 19 Bucles printf("Selecciona un valor de los mostrados a continuacion:\n"); while (k<19) { printf("%d ", datos[k]); k++; }/*fin while*/ printf("\n"); scanf("%d", &entrada); k = 1; posicion =(maximo-minimo+1)/2; while ( entrada != datos[posicion] ) if (datos[posicion] > entrada) { k++; maximo = posicion; posicion = posicion - (maximo - minimo+1)/2; }/*fin if*/ else if (datos[posicion] < entrada) { k++; minimo = posicion; posicion = posicion + (maximo - minimo+1)/2; }/*fin else if*/ printf("\nEl dato encontrado ha sido el %d.\nEl numero de pasos para" "encontrarlo ha sido %d", datos[posicion], k); getch(); }/*fin main*/ Bucles • Modificar el programa anterior para que, en caso de que el numero introducido no pertenezca a la lista, lo indique. Además añadir la opción de que el usuario pueda repetir la búsqueda las veces que desee. • Formato de for: – for (inicialización variable; condicion de test; expresión de modificación de la variable) { sentencias; } • La inicialización se realiza sólo una vez al comienzo del bucle. 20 Bucles • La condición de test se realiza antes de cada posible ejecución del bucle. Si la condición es falsa, el bucle finaliza. • La expresión de modificación de la variable se evalúa al final de la ejecución de cada bucle. • Ejemplo: – for(n=1; n<=1000;n++); { sentencias; } • El bucle for da mucha flexibilidad: – for (n = 10; n > 0; n--) Bucles. • El bucle for da mucha flexibilidad (continuación): – – – – – for (n = 1; n < 60; n + = 10) for (n = 1; n < 1000; n *= 10) for (ch = ‘a’; ch <= ‘z’; ch++) for (n = 1; n*n < 1000; n++) for (n = 1; n*n > 10; ) ⇒ Hay que introducir la expresión de modificación de la variable dentro del bucle. – for (printf(“Introducir numero”); n = 10; ) scanf(“%d”, &n); – for (n = 0, m = 1; n < 10; n++, n *= 10) • Las dos estructuras de bucles ( while y for) permiten anidamiento; 21 Bucles • Ejemplo6: /* * */ EJEMPLO6.c #include <stdio.h> #include <conio.h> void main(void) { float a[100][100], b[100][100]; /*Matriz a y b de entrada*/ int filas = -1, columnas = -1; /*Numero de filas y columnas de las matrices*/ int f, c; /*Indices del for*/ char matriz; while ( ((filas == -1) && (columnas == -1)) || ((filas >= 100) || (columnas >= 100)) ) { printf("Numero de filas? (maximo = 100)\n"); scanf("%d", &filas); printf("Numero de columas? (maximo = 100)\n"); scanf("%d", &columnas); } Bucles for(matriz = 'a'; matriz < 'c'; matriz++) for(f = 0; f < filas; f++) for(c = 0; c < columnas; c++) switch (matriz){ case 'a': printf("a[%d,%d] = ", f+1, c+1); scanf("%f", &a[f][c]); printf("\n"); break; case 'b': printf("b[%d,%d] = ", f+1, c+1); scanf("%f", &b[f][c]); printf("\n"); } for(f = 0; f < filas; f++) for(c = 0; c < columnas; c++) printf("s[%d,%d] = %f\n", f+1, c+1, a[f][c]+b[f][c]); getch(); } 22 Funciones y Punteros. • Hasta ahora funciones implementadas en las librerías estándar de C: printf, scanf ... • Formato de una función: tipo_función nombre_función (lista de parámetros) { Cuerpo de la función; } • Con tipo_función especificamos el tipo del dato que devuelve la función. Para devolver el dato desde la función hay que utilizar la sentencia return(dato_devuelto); En el caso de que la función no devuelva ningún dato se utiliza el tipo void. Funciones y Punteros. • La lista de parámetros es la lista de nombres de variables, separadas por comas, con sus tipos asociados que reciben los valores de los argumentos cuando se llama a la función. En el caso de que no existan parámetros de entrada a la función se utiliza void. • Ejemplo: int EsMayor(int x, int y) { if (x > y) return(1); else return(0); } 23 Funciones y Punteros. • Dos métodos para pasar argumentos a las funciones: – Llamada por valor. – Llamada por referencia. • Cuando se hace una llamada por valor, lo que se hace es copiar el valor de un argumento en el parámetro formal de la función (ejemplo anterior). Este método se utiliza cuando no se pueden, o no necesitamos, modificar las variables usadas para llamar a la función. Funciones y Punteros. • Con la llamada por referencia lo que se hace es copiar la dirección del argumento en el parámetro formal de la función. Dentro de la función se usa esta dirección para acceder al argumento usado en la llamada. Debido al hecho de que dentro de la función no trabajamos con la variable, sino con su dirección en memoria, vamos a poder modificar los valores de dichos parámetros. • Ejemplo de llamada por referencia: 24 Funciones y Punteros. void main(void) { int mayor, menor, igual; ... igual = Ordena(&mayor, &menor); ... } int Ordena(int *x, int *y) { int auxiliar; if (*x < *y) { auxiliar = *x; *x = *y; *y = auxiliar; return(0); } else if (*x == *y) return(1); } Funciones y Punteros. • Un puntero es una variable que contiene una posición de memoria • Declaración de variable puntero: tipo *nombre_variable; • tipo es cualquier tipo válido de C y define el tipo válido al que puede apuntar el puntero. • Operadores de punteros: – & – * • El operador & es un operador monario que devuelve la dirección de memoria de su operando. Por ejemplo: m = &cuenta; 25 Funciones y Punteros. • El operador * es el complemento de &. Es un operador monario, que devuelve el valor de la variable localizada en la dirección que marca su operando. Por ejemplo: q = *m; • Un puntero puede utilizarse a la derecha de una operación de asignación`para asignar su valor a otro puntero. Por ejemplo: int x; int *p1, *p2; p1 = &x; p2 = p1; Funciones y Punteros. • Arimética de punteros: sólo es valido el uso de la suma y la resta con enteros. • Cuando realizamos una suma o una resta sobre un puntero lo que hacemos es que dicho puntero apunte a la posición de memoria posterior o anterior indicada por la operación, teniendo en cuenta los bytes que ocupan su tipo asociado. • Ejemplo: char *p1 = 0x2000; /* 1 char = 1 byte */ int *p2 = 0x3000; /* 1 int = 4 bytes */ p1++; /* p1 = 2001 */ p2++; /* p2 = 0x2004 */ 26 Funciones y Punteros. • Es valido la utilización de operadores de relación con los punteros. • Está permitida la indirección múltiple, es decir, un puntero que apunte a otro puntero. • Ejemplo: void main(void) { int x = 10, *p1, **p2; p1 = &x; p2 = &p2; printf(“%d”, **p2); } Funciones y Punteros. • La inicialización de un puntero es una fase crítica, ya que en el caso de no inicializarlo bien, vamos a acceder a posiciones de memoria no validas, lo cual llevará o, al mal funcionamiento del programa, o a un error grave en el sistema. • Por convenio se asigna el valor nulo (\0) para inicializarlo, indicado que no apunta a nada. • Los punteros es una de las herramientas más potente de C, pero es también la que nos puede producir los errores más complicados de encontrar. 27 Funciones y Punteros. • Es necesario siempre que usemos una función definir su prototipo. • El prototipo de una función no es más que la declaración del número y tipos de los argumentos utilizados en dicha función. De esta forma se puede realizar una comprobación de estos parámetros para evitar errores. • Formato para la definición del prototipo de una función: tipo_función nombre_función (tipo parámetro 1, ..., tipo parámetro n); • Por ejemplo: – int Ordena(int *, int *); Funciones y Punteros. • Ejemplo7: /* * */ EJEMPLO7.c #include <stdio.h> #include <conio.h> void Numeros(int *, int *); int Compara(int *, int *); void Muestra(int, int, int); void main(void) { int mayor = 0, menor = 0; int es_igual; Numeros(&mayor, &menor); es_igual = Compara(&mayor, &menor); Muestra(es_igual, mayor, menor); getch(); } 28 Funciones y Punteros. void Numeros(int *x, int *y) { printf("x = "); scanf("%d", x); printf("y = "); scanf("%d", y); } int Compara(int *x, int *y) { int auxiliar; if (*x == *y) return(1); if (*x < *y) { auxiliar = *x; *x = *y; *y = auxiliar; } return(0); } void Muestra(int iguales, int mayor, int menor) { if (iguales) printf("Ambos numeros son iguales"); else printf("El valor %d es mayor que el valor %d", mayor, menor); } El Preprocesador de C • Se pueden incluir varias instrucciones dirigidas al compilador en el código fuente. Se denominan directivas de preprocesamiento. • El preprocesador contiene las siguientes directivas: #if #else #define #error #ifdef #elif #undef #pragma #ifndef #include #line • Todas empiezan con #. • Cada directiva de preprocesamiento debe estar en su propia línea. 29 El Preprocesador de C • La directiva #define, define un identificador y una cadena que será sustituida por el identificador cada vez que se encuentre en el archivo fuente. • Formato: #define nombre_de_macro cadena • Ejemplos: – #define TRUE 1 – #define UNO 1 #define DOS UNO+UNO • El nombre de macro puede tener argumentos • Ejemplos: – #define ABS(a) (a) < 0 ? -(a) : (a) – #define CUADRADO(x) x*x El Preprocesador de C. • La directiva #include hace que el compilador incluya otro archivo fuente al propio que tiene a esta directiva. • Formato: #include <fichero> #include “fichero” • Lo normal es incluir lo que se denominan ficheros de cabecera. Estos ficheros tienen definidos otras directivas así como prototipos de funciones. • Ejemplos: – #include <stdio.h> (está definido el prototipo de printf y scanf). – #include <conio.h> (está definido el prototipo de getch) 30 El Preprocesador de C. • Si el fichero de cabecera esta definido entre < y > el compilador busca el fichero en un directorio definido por defecto. • Si el fichero de cabecera esta definido entre comillas dobles el compilador busca el fichero en el mismo directorio donde se encuentra el resto de código fuente. Se utiliza cuando hemos creado nosotros mismo el fichero de cabecera. • Siempre se puede especificar el directorio donde se encuentra el fichero de cabecera. El Preprocesador de C. • La directiva #error fuerza al compilador a parar la compilación y se muestra el mensaje de error definido en la propia directiva. (#error mensaje de error) • Si la constante que sigue a #if es cierta, se compila el código que hay entre el #if y el #endif. En caso contrario se ignora este código. Al evaluarse en tiempo de compilación no pueden utilizarse variables para la evaluación. Sólo pueden usarse constantes e identificadores definidos anteriormente. • #else es equivalente a else. • #elif es equivalente a else if. 31 El Preprocesador de C. • #ifdef (si definido) e #ifndef (si no definido) es otro método de compilación condicional. Chequea si se ha definido o no una macro con #define, compilandose el bloque de sentencias en caso de ser cierto. Al igual que con #if el bloque a compilar termina con #endif. Puede usarse también #else, aunque no #elif • La directiva #undef elimina una definición de macro definida anteriormente con #define. • #line modifica los contenidos de los identificadores predefinidos _LINE_ y _FILE_ • #pragma es una directiva definida por la implementación del compilador y que permite que se le de ordenes a este. Arrays , Estructuras y Uniones. • Un array en una serie de datos del mismo tipo. • A la hora de definirlo es necesario especificar no sólo el tipo de dato a almacenar, sino el número a almacenar. • Definición e inicialización de un array: tipo_dato nombre_array[número de datos] = {primer elemento, segundo elemento,...}; tipo_dato nombre_array[] = {primer elemento, segundo elemento,...}; • Llamada a un elemento de un array: x[i], donde i es el índice. Para el primer elemento i = 0 y para el último i =numero de datos-1. int x[3] = {1, 2, 3}; printf(“x = %d, y = %d, z = %d”, x[0], x[1], x[2]); 32 Arrays , Estructuras y Uniones. • Los arrays y los punteros están muy relacionados, ya que el nombre de un array es también un puntero que apunta al primer elemento del array. Es decir, si tenemos definido un array x de tres elementos ⇒ x = = &x[0], x + 1 = = &x[1], x + 2 = = &x[2], *x = = x[0], *(x+1) = = x[1] y *(x+2) = = x[2]. • El uso de punteros sobre arrays es más mucho eficiente. • La llamada a una función que trabaja sobre un array se realiza pasandole como argumento el nombre del array, es decir, le pasamos un puntero. La función usa este puntero para realizar los cambios sobre el array original. Arrays , Estructuras y Uniones. • Ejemplo: int x[3]; funcion(x); • Existen dos formas de definir la variable puntero que trabajará sobre el array: tipo_función nombre_función (tipo *nombre_array) { Cuerpo función } tipo_función nombre_función (tipo nombre_array[]) { Cuerpo función } 33 Arrays , Estructuras y Uniones. • Ejemplo 8: /* * */ Ejemplo8.c #include <stdio.h> #include <conio.h> #define NUMERO_DATOS 10 float Maximo(float *); float Suma(float *); void main(void) { float datos[]={1.9, 5.9, 8.7, 3.6, 23.6, -2.9, -12.1, 33.4, 42.7, -56.5}; printf("El maximo valor de la lista es el %f\n", Maximo(datos)); printf("La suma de los elementos de la lista es %f\n", Suma(datos)); getch(); } Arrays , Estructuras y Uniones. float Maximo(float *datos) { int k; float max = 0.0; for(k = 0; k < NUMERO_DATOS; k++) if ( *(datos+k) > max ) max = *(datos+k); return(max); } float Suma(float *datos) { int k; float suma; for(k = 0; k < NUMERO_DATOS; k++) suma += *(datos+k); return(suma); } 34 Arrays , Estructuras y Uniones. • Es posible la definición de arrays multidimensionales (array de array). • Definición e inicialización : tipo_array nombre_array[n1] [n2]...[nm]; tipo_array nombre_array[] []...[] = {{elem111, elem112, ..., elem11m}, {elem121, elem122,..., elem12m},...}; • Ejemplo: – int x[2] [2] = {{1,2}, {3,4}}; • La llamada a un elemento del array se hace de forma equivalente a un array unidimensional: x[i][j]. Arrays , Estructuras y Uniones. • Con respecto a los punteros se verifica que: – x == x[0] == &x [0][0] – x [1] == &x [1] [0] • La definición de un puntero equivalente al array anterior sería: – int (*x) [2] ; • Una estructura es una colección de variables que se referencia bajo un único nombre. • La definición de una estructura forma una plantilla que puede utilizarse para crear variables de estructura. • Las variables que forman una estructura deben estar relacionadas. 35 Arrays , Estructuras y Uniones. • Definición de una estructura: – struct nombre_estructura{ tipo nombre_variable_1; tipo nombre_variable_2; ... tipo nombre_variable_n; }; struct nombre_estructura nombre_variable_estructura; – struct nombre_estructura{ tipo nombre_variable_1; tipo nombre_variable_2; ... tipo nombre_variable_n; } nombre_variable_estructura_1, nombre_variable_estructura_2,...; Arrays , Estructuras y Uniones. • Ejemplos: – struct ficha{ char nombre[20]; char apellidos [60]; int edad; }; struct ficha PrimeraFicha; – struct ficha{ char nombre[20]; char apellidos [60]; int edad; } PrimeraFicha; 36 Arrays , Estructuras y Uniones. • La forma de referenciar a un elemento de la estructura es: – PrimeraFicha.nombre[0]; – PrimeraFicha.edad; • Es posible definir arrays de estructuras. Para ello debe estar definido en primer lugar la estructura y luego definir una variable array de dicho tipo. • Ejemplo: – struct ficha Fichas[100]; • En este caso la referencia se realiza de la siguiente forma: – Fichas[0].edad; Arrays , Estructuras y Uniones. • Para pasar un elemento de una estructura a una función se hace de forma equivalente a como hemos visto hasta ahora. • Para pasar una estructura completa como argumento a una función se hace mediante una llamada por valor o por referencia. • En el primer caso los cambios realizados en la función no afectan al valor original de la estructura. 37 Arrays , Estructuras y Uniones. • Ejemplo: struct estructura{ char ch; int x, y; } nombre; void main (void) { ... funcion(nombre); ... } void funcion(struct estructura arg) { ... } Arrays , Estructuras y Uniones. • Para realizar una llamada por referencia es necesario definir un puntero a una estructura. • Para definir la variable puntero a la estructura se hace deforma equivalente ha lo visto hasta ahora: – struct nombre_estructura *nombre_variable; • Una vez definido el puntero a la estructura, el acceso a los elementos de dicha estructura se realiza de la siguiente forma: struct estuctura{ char ch; int x, y; } *nombre; nombre->ch; nombre->x; 38 Arrays , Estructuras y Uniones. • Ejemplo 9: /* * */ EJEMPLO9.C #include <stdio.h> #include <conio.h> struct fichas{ char nombre[15]; char apellidos[50]; int edad; }; void IncluirDatos(struct fichas *, int); void MostrarDatos(struct fichas *, int); void main(void) { struct fichas Fichas[2]; IncluirDatos(Fichas,2); MostrarDatos(Fichas,2); getch(); } Arrays , Estructuras y Uniones. void IncluirDatos(struct fichas *Fichas, int nfichas) { int k; for(k=0; k < nfichas; k++) { printf("Nombre = "); scanf("%s", &((&Fichas[k])->nombre)); printf("Apellidos = "); scanf("%s", &((&Fichas[k])->apellidos)); printf("Edad = "); scanf("%d", &((&Fichas[k])->edad)); printf("\n"); } } void MostrarDatos(struct fichas *Fichas, int nfichas) { int k; for(k=0; k < nfichas; k++) { printf("Nombre = %s\n", (Fichas+k)->nombre ); printf("Apellidos = %s\n", (Fichas+k)->apellidos); printf("Edad = %d\n\n", (Fichas+k)->edad); } } 39 Arrays , Estructuras y Uniones. • Una unión es una posición de memoria que es compartida por dos o más variable diferentes, generalmente de distinto tipo, en distintos momentos. • La definición es similar a la de estructura: union nombre{ tipo variable_1; tipo variable_2; ... tipo variable_n; } nombre_variable_union; • El acceso a las variables y los puntero son identicos que con las estructuras. 40