Download Lenguaje C - FI
Document related concepts
no text concepts found
Transcript
Universidad Nacional Autónoma de México Facultad de Ingeniería Lenguaje C Autor: Ing. Israel Durán Martínez Creado en Octubre 2007 Última actualización: Agosto 2010 ÍNDICE ÍNDICE ............................................................................................................................... 2 CAPÍTULO 1: INTRODUCCIÓN AL LENGUAJE C ....................................... 5 1.1 ORÍGENES DEL LENGUAJE C ........................................................................................... 5 1.2 UN LENGUAJE DE NIVEL MEDIO ...................................................................................... 5 1.3 UN LENGUAJE ESTRUCTURADO ...................................................................................... 6 1.4 COMPILADORES FRENTE A INTÉRPRETES ........................................................................ 7 1.5 LA BIBLIOTECA Y EL ENLAZADO .................................................................................... 8 1.6 ENTORNOS DE DESARROLLO .......................................................................................... 9 CAPÍTULO 2: LENGUAJE C: ELEMENTOS BÁSICOS .............................. 10 2.1 ESTRUCTURA GENERAL DE UN PROGRAMA EN C .......................................................... 10 2.1.1 Directivas del preprocesador .............................................................................. 11 2.1.2 Declaraciones globales ....................................................................................... 11 2.1.3 Función main( ) ................................................................................................... 11 2.1.4 Funciones definidas por el usuario ..................................................................... 11 2.2 COMENTARIOS ............................................................................................................. 13 2.3 IDENTIFICADORES ........................................................................................................ 13 2.4 PALABRAS RESERVADAS .............................................................................................. 14 2.5 TIPOS DE DATOS ........................................................................................................... 14 2.6 VARIABLES .................................................................................................................. 17 2.6.1 Variables locales ................................................................................................. 17 2.6.2 Variables globales ............................................................................................... 18 2.6.3 Inicialización de variables .................................................................................. 19 2.6.4 Constantes ........................................................................................................... 20 2.6.5 Constantes de carácter de barra invertida .......................................................... 21 CAPÍTULO 3: OPERADORES Y EXPRESIONES ......................................... 23 3.1 OPERADORES ARITMÉTICOS ......................................................................................... 23 3.2 OPERADORES RELACIONALES Y LÓGICOS ..................................................................... 23 3.3 OPERADORES A NIVEL DE BITS ..................................................................................... 24 3.4 PRECEDENCIA DE LOS OPERADORES DE C .................................................................... 25 CAPÍTULO 4: INSTRUCCIONES DE CONTROL ............................................ 26 4.1 INSTRUCCIONES DE SELECCIÓN .................................................................................... 26 IF .................................................................................................................................. 26 SWITCH ........................................................................................................................ 26 4.2 INSTRUCCIONES DE ITERACIÓN .................................................................................... 27 FOR .............................................................................................................................. 27 WHILE .......................................................................................................................... 28 DO WHILE ................................................................................................................... 29 4.3 INSTRUCCIONES DE SALTO ........................................................................................... 29 acultad de Inge return ............................................................................................................................ 29 break ............................................................................................................................. 30 continue ........................................................................................................................ 30 CAPÍTULO 5: FUNCIONES ..................................................................................... 31 5.1 FORMA GENERAL DE UNA FUNCIÓN.............................................................................. 31 5.2 PROTOTIPOS DE LAS FUNCIONES .................................................................................. 32 5.3 PARÁMETROS DE UNA FUNCIÓN ................................................................................... 32 5.4 ARGUMENTOS DE MAIN() ............................................................................................. 35 CAPÍTULO 6: ARRAYS Y CADENAS................................................................... 38 6.1 ARRAYS UNIDIMENSIONALES ....................................................................................... 38 6.2 ALMACENAMIENTO EN MEMORIA DE LOS ARRAYS ....................................................... 39 6.3 TAMAÑO DE ARRAYS ................................................................................................... 39 6.4 INICIALIZACIÓN DE UN ARRAY ..................................................................................... 40 6.5 CADENAS ..................................................................................................................... 41 6.6 ARRAYS BIDIMENSIONALES ......................................................................................... 41 6.7 INICIALIZACIÓN DE ARRAYS BIDIMENSIONALES ........................................................... 42 CAPÍTULO 7: PUNTEROS........................................................................................ 43 7.1 ¿QUÉ SON LOS PUNTEROS? ........................................................................................... 43 7.2 DECLARACIÓN DE APUNTADORES ................................................................................ 43 7.3 PUNTEROS Y ARRAYS .................................................................................................. 45 7.4 PUNTEROS DE CADENAS ............................................................................................... 46 7.5 PUNTEROS A FUNCIONES .............................................................................................. 46 CAPÍTULO 8: ESTRUCTURAS ............................................................................... 48 8.1 DEFINICIÓN .................................................................................................................. 48 8.2 ACCESO A LOS MIEMBROS DE LA ESTRUCTURA ............................................................ 49 8.3 ASIGNACIÓN DE VALORES ............................................................................................ 49 8.4 PUNTEROS A ESTRUCTURAS ......................................................................................... 50 CAPÍTULO 9: ARCHIVOS ........................................................................................ 53 9.1 MANEJO DE ARCHIVOS ................................................................................................ 53 fopen()........................................................................................................................... 54 fclose() .......................................................................................................................... 54 putc()............................................................................................................................. 54 fputc() ........................................................................................................................... 54 getc() ............................................................................................................................ 55 fgetc() ........................................................................................................................... 55 fgets() ........................................................................................................................... 56 fputs() ........................................................................................................................... 56 fseek() .......................................................................................................................... 57 fwrite() ......................................................................................................................... 60 fflush() ......................................................................................................................... 61 9.2 EL PUNTERO A ARCHIVO .............................................................................................. 61 9.3 APERTURA DE UN ARCHIVO ......................................................................................... 66 acultad de Inge 9.4 CIERRE DE UN ARCHIVO ............................................................................................... 67 BIBLIOGRAFÍA ............................................................................................................ 71 acultad de Inge CAPÍTULO 1: INTRODUCCIÓN AL LENGUAJE C 1.1 Orígenes del lenguaje C El lenguaje C fue inventado e implementado por primera vez por Dennis Ritchie en DEC PDP-11 usando UNIX como sistema operativo. C es el resultado de un proceso de desarrollo comenzando con un lenguaje anterior denominado BCPL. BCPL fue desarrollado por Martín Richards e influenció otro lenguaje denominado B, inventando por Ken Thompson, que en los años sesenta llevó al desarrollo del C. Durante muchos años el estándar de C fue realmente la versión proporcionada con la versión V del sistema operativo UNIX. Aparece descrito en el libro The C Programming Language de Brian Kernighan y Dennis Ritchie. Con la popularidad de las microcomputadoras se crearon muchas implementaciones de C. En lo que se podría decir que era un milagro, los códigos fuente aceptados por la mayoría de esas implementaciones eran altamente compatibles. Sin embargo, como no existía ningún estándar, aparecieron discrepancias. Para remediar la situación, el Instituto Nacional de Estándares Americano (ANSI) estableció un comité a principios del verano de 1983 para crear un estándar que definiera de una vez por todas el lenguaje C. Finalmente, el estándar fue adoptado en diciembre de 1989, comenzando a estar disponibles las primeras copias a principios de 1990. El estándar también fue adoptado por la ISO (Organización Internacional de Estándares), siendo habitual que el estándar se refiera como estándar ANSI/ISO. Durante los años noventa, el desarrollo del estándar de C++ acaparó casi toda la atención de los programadores. Sin embargo, el trabajo sobre C continuó, siendo desarrollado un nuevo estándar terminado en 1999 y que comúnmente se le llama C99. En general, C99 mantiene prácticamente todas las características de C89. 1.2 Un lenguaje de nivel medio A menudo se denomina al lenguaje C como un lenguaje de computadora de nivel medio. Esto no significa que sea menos potente, más difícil de usar o menos evolucionado que lenguajes de alto nivel como BASIC o Pascal; ni implica que C sea acultad de Inge similar al lenguaje ensamblador y por tanto presente al usuario sus problemas asociados. C se presenta como un lenguaje de nivel medio porque combina elementos de lenguajes de alto nivel con la funcionalidad del lenguaje ensamblador. Como lenguaje de nivel medio, C permite la manipulación de bits, bytes y direcciones – los elementos básicos con los que funciona la computadora-. El código de C es muy portable. Todos los lenguajes de alto nivel soportan el concepto de tipos de datos. Un tipo de datos define un conjunto de valores que pueden tener una variable junto con un conjunto de operaciones que se pueden realizar sobre esa variable. Algunos tipos de datos comunes son los enteros, los caracteres y los reales. Aunque el lenguaje C tiene cinco tipos de datos básicos incorporados, no se trata de un lenguaje fuertemente tipificado en el sentido de Pascal o Ada. C permite casi todas las conversiones de tipos. Por ejemplo, los tipos entero y de carácter pueden ser entremezclados libremente en la mayoría de las expresiones. C no lleva a cabo la comprobación de errores en el tiempo de ejecución como pueden ser el sobrepasar los límites de los arrays o la compatibilidad de tipos de los argumentos. Esas comprobaciones quedan enteramente como responsabilidad del programador. 1.3 Un lenguaje estructurado El lenguaje C se denomina normalmente lenguaje estructurado, ya que tiene similitudes estructurales con ALGOL, Pascal y Modula-2. Técnicamente, un lenguaje estructurado en bloques permite declarar procedimientos o funciones dentro de otros procedimientos o funciones. De esta forma, se extienden los conceptos de globalidad y localidad mediante el uso de reglas de alcance, que gobiernan la visibilidad de las variables o de los procedimientos. Dado que C no permite la creación de funciones dentro de funciones, no puede ser formalmente denominado lenguaje estructurado en bloques. La característica distintiva de un lenguaje estructurado es la compartimentalización de código y datos. Se trata de la capacidad de un lenguaje de seccionar y esconder el resto del programa toda la información y las instrucciones necesarias para llevar a cabo una determinada tarea. Una forma como se consigue la acultad de Inge compartimentalización es mediante el uso de subrutinas que empleen variables locales. Con el uso de variables locales es posible escribir subrutinas de forma que lo que ocurra en su interior no provoque efectos secundarios en otras partes del programa. Esta posibilidad hace que sea muy fácil que los programas en C compartan secciones de código. Al desarrollar funciones compartimentalizadas sólo es necesario conocer que es lo que la función hace, no cómo lo hace. El excesivo uso de variables globales puede hacer que los errores proliferen en el programa, al permitir los efectos secundarios no deseados. La componente estructural principal de C es la función; una subrutina independiente. En C, las funciones son los bloques constitutivos en los que se desarrolla toda la actividad de los programas. Son las que permiten definir las tareas de un programa y codificarlas por separado, permitiendo así que los programas sean modulares. Una vez que se ha creado una función se puede aprovechar funcionando perfectamente en distintas situaciones, sin crear efectos secundarios en otras partes del programa. El hecho de poder crear funciones independientes es algo extremadamente crítico en grandes proyectos donde es importante que el código de un programador no afecte accidentalmente al de otro. Otra forma de estructuración y compartimentalización de código en C viene dada por el uso de bloques de código. Un bloque de código es un grupo de instrucciones de un programa conectadas de forma lógica que es tratado como una unidad. En C se crean bloques de código colocando una serie de instrucciones entre llaves. Ejemplo: if(x < 10) { printf(“Demasiado bajo \n”); } 1.4 Compiladores frente a intérpretes Los términos compilador e intérprete se refieren a la forma en que se ejecuta un programa. En teoría, cualquier lenguaje de programación puede ser compilado o interpretado, pero algunos se suelen ejecutar en una forma o en la otra. Por ejemplo, acultad de Inge Java fue diseñado para ser interpretado y C para ser compilado. La forma en que se ejecuta un programa no viene definida por el lenguaje en que se haya escrito. Los intérpretes y los compiladores con simplemente programas que trabajan sobre el código fuente del programa. Un intérprete lee el código fuente de un programa línea a línea, realizando las instrucciones específicas contenidas en esa línea. Un compilador lee el programa entero y lo convierte en código objeto, que es una traducción del código fuente del programa a una forma que puede ser ejecutada directamente por la computadora. El código objeto también se suele denominar código binario o código máquina. Una vez que el programa está compilado las líneas del código fuente dejan de tener sentido en la ejecución del programa. Cuando se usa un intérprete, el código fuente debe estar presente cada vez que se quiere ejecutar el programa. Por el contrario, un compilador convierte el programa en código objeto que puede ser directamente ejecutado por la computadora. Como el compilador traduce el programa de una vez, todo lo que hay que hacer es ejecutarlo directamente, normalmente mediante el simple proceso de escribir su nombre. Por tanto, la compilación sólo cuesta una vez, mientras que el código interpretado lleva esa carga cada vez que se ejecuta un programa. 1.5 La biblioteca y el enlazado Técnicamente hablando, es posible crear un programa en C que sea funcional y útil y que consista solamente en las sentencias realmente creadas por el programador. Sin embargo, esto es bastante raro, ya que C no proporciona, dentro de la definición real del lenguaje, ningún método para llevar a cabo las operaciones de entrada/salida. Por ello la mayoría de los programadores incluyen llamadas a varias funciones contenidas en la biblioteca estándar del lenguaje C. Las funciones que se encuentran en la biblioteca lo están en formato reubicable. Esto significa que las direcciones de memoria de las diferentes instrucciones de código máquina no han sido definidas de forma absoluta; solo se mantiene información sobre desplazamientos. Cuando se enlaza un programa con funciones de la biblioteca estándar, se usan esos desplazamientos de memoria para definir las direcciones reales. acultad de Inge 1.6 Entornos de desarrollo La mayoría de los compiladores modernos se proporcionan en dos formas. La primera forma es el compilador autónomo de línea de órdenes. Con esta forma se utiliza un editor aparte para crear los programas, luego se compila el programa y, finalmente, se ejecuta el programa. Esto se lleva a cabo mediante órdenes independientes dadas en la línea de comandos. Esta es la forma tradicional de implementación de los compiladores. La segunda forma de un compilador se encuentra en un entorno integrado de desarrollo (IDE), como Visual C++ o Dev C++. En esta forma, el compilador esta integrado con un editor, un depurador, un administrador de proyectos y un sistema de soporte para tiempo de ejecución. Con un IDE se edita, compila y ejecuta el programa sin salir del entorno. Actualmente los IDEs que distribuyen los principales fabricantes de compiladores tienen mucho que ofrecer a los programadores. Si se emplea un poco de tiempo en ajustar las opciones del entorno para que esté optimizado a las necesidades propias, se puede comprobar que el uso del IDE ayuda mucho en el proceso de desarrollo. acultad de Inge CAPÍTULO 2: LENGUAJE C: ELEMENTOS BÁSICOS 2.1 Estructura general de un programa en C Todos los programas en C consisten en uno o más módulos llamados funciones. La única función que debe estar absolutamente presente es la denominada main(), siendo la primera función que es llamada cuando comienza la ejecución del programa. En código C bien escrito, main() en esencial, esboza lo que el programa hace. Ese esbozo está compuesto por llamadas a funciones. Aunque técnicamente main() no es una palabra clave, se le trata como si lo fuera. No se debe usar main() como nombre para una variable ya que el compilador se confundirá. La forma general de un programa en C se muestra a continuación: Directivas del preprocesador Prototipos de funciones Declaraciones globales /*Función main*/ int main(void) { variables locales sentencias llamadas a funciones } /*Funciones definidas por el usuario*/ f1() { variables locales sentencias secuencia de sentencias } f2() { variables locales sentencias secuencia de sentencias } . . . fN() { variables locales sentencias secuencia de sentencias } acultad de Inge 2.1.1 Directivas del preprocesador El preprocesador consta de directivas que son instrucciones al compilador. Todas las directivas del preprocesador comienzan con el signo de libro o de <<almohadilla>> (#) y no terminan en punto y coma, ya que son instrucciones del lenguaje C. La directiva #include indica al compilador que lea el archivo fuente (archivo cabecera o de inclusión) que viene a continuación de ella y su contenido lo inserte en la posición donde se encuentra dicha directiva. Estas instrucciones son de la forma #include <nombrearchivo.h> o bien #include “nombrearchivo.h”. La directiva #define indica al preprocesador que defina un ítem de datos u operación para el programa C. Por ejemplo, la directiva #define TAM 10 sustituirá el valor de 10 cada vez que TAM aparezca en el programa. 2.1.2 Declaraciones globales Las declaraciones globales indican al usuario que las constantes o variables así declaradas son comunes a todas las funciones de su programa. Se sitúan antes de la función main( ). La zona de declaraciones globales puede incluir declaraciones de variables además de declaraciones de prototipos de función. 2.1.3 Función main( ) Cada programa C tiene una función main( ) que es un punto inicial de entrada al programa. Su estructura es: int main() { . . . Bloque de sentencias return 0; } 2.1.4 Funciones definidas por el usuario acultad de Inge Un programa C es una colección de funciones. C proporciona funciones predefinidas que se denominan funciones de biblioteca y otras definidas por el usuario. Se invocan por su nombre y los parámetros opcionales que tenga. Después de que la función sea llamada, el código asociado con la función se ejecuta y, a continuación, se retorna a la función llamadora. En C, las funciones definidas por el usuario requieren una declaración o prototipo en el programa, que indica al compilador el nombre por el que ésta será invocada. #include<stdio.h> //Prototipo de la función void prueba(); int main() { prueba(); return 0; } //Función definida por el usuario void prueba() { printf(“Mis primeros pasos \n”); } Ejemplo: Mi primer programa en C NOTA: Todos los programas están compilados en Dev C++ /*Directivas del preprocesador*/ #include<stdio.h> /*Prototipos de funciones*/ void hola(void); /*Función main*/ int main(void) { printf("Mi primer programa en C\n"); hola(); getchar(); return 0; } /*Funciones definidas por el usuario*/ void hola(void) { printf("Hola a todos\n"); } acultad de Inge 2.2 Comentarios Los comentarios de una sola línea comienzan con // y terminan al final de la línea. Por ejemplo: //Este es un comentario de una sola línea Los comentarios multilínea empiezan con un /* y terminan con */. No debe haber espacios entre el asterisco y la barra. El compilador ignora cualquier texto entre los símbolos de comienzo y fin de comentario. Los comentarios multilínea no se pueden anidar. Es decir, un comentario no puede contener otro comentario. Por ejemplo: /* Comentario externo /*Comentario interno que provoca un error*/ */ 2.3 Identificadores Los nombres usados para referirse a las variables, funciones, las etiquetas y otros objetos definidos por el usuario se conocen como identificadores. El primer carácter debe ser una letra o un símbolo de subrayado y los caracteres siguientes pueden ser letras, números o símbolos de subrayado. En C las minúsculas y las mayúsculas se tratan como distintas. Así, cuenta, Cuenta y CUENTA son tres identificadores distintos. Además de que un identificador no puede ser igual que una palabra clave y no debe tener el nombre de una función ya escrita. Ejemplo: a) Identificadores válidos X y21 suma_1 _temperatura nombres area porc_imp TABLA b) Identificadores no válidos 4num El primer carácter debe ser letra. “x” Caracteres ilegales (“) acultad de Inge orden-no Carácter ilegal (-) indicador error Carácter ilegal (espacio en blanco) Convenciones Los identificadores deben ser descriptivos: deben hacer referencia al significado de aquello a lo que se refieren. int n1, n2 //MAL int anchura, altura; //BIEN Los identificadores asociados a las variables se suelen poner en minúsculas int ConTADoR; //MAL int contador; //BIEN Cuando el identificador está formado por varias palabras, la primera palabra va en minúsculas y el resto de palabras se inician con una letra mayúscula (o bien se separan las distintas palabras por guiones de subrayado. int mayorvalor; //MAL int mayor_valor; //BIEN int mayorValor; //BIEN 2.4 Palabras reservadas Las siguientes palabras no pueden usarse como identificadores, es decir, no debe servir como nombre de variable o de función. auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if static while Todas las palabras van en minúsculas. 2.5 Tipos de datos acultad de Inge Existen cinco tipos de datos atómicos en C: carácter, entero, coma flotante, coma flotante de doble precisión y sin valor Tipo Tamaño en bytes Rango char 1 -128 a 127 int 2 -32768 a 32767 float 4 1.17549e–38 a 3.40282e+38 double 8 2.22507e–308 a 1.79769e+308 void 0 Sin valor Los valores de tipo char se usan normalmente para guardar valores definidos en el juego de caracteres ASCII, así como cualquier cantidad de 8 bits. Las variables de tipo int se usan para guardar cantidades enteras. Las variables de tipo float o double se usan para guardar números reales. El tipo void tiene tres usos. El primero es para declarar explícitamente una función como que no devuelve valor alguno; el segundo es para declarar explícitamente una función sin parámetros; el tercero es para crear punteros genéricos. Modificadores de tipos A excepción del tipo void, los tipos de datos básicos pueden tener distintos modificadores precediéndolos. Un modificador se usa para alterar el significado del tipo base para que se ajuste más precisamente a las necesidades de cada momento. A continuación se muestra la lista de modificadores: signed – Aplicable a los tipos char, short, int y long. unsigned – Aplicable a los tipos char, short, int y long. long short Los modificadores signed, unsigned, short y long se pueden aplicar a los tipos base entero y de carácter. Sin embargo, long también se puede aplicar a double. acultad de Inge Tipo Tamaño en bytes Rango char 1 -128 a 127 unsigned char 1 0 a 255 signed char 1 -128 a 127 int 2o4 -32,768 a 32,767 unsigned int 2o4 0 a 65,535 signed int 2o4 Igual que int short int 2 -32,768 a 32,767 unsigned short int 2 0 a 65,535 signed short int 2 Igual que short int long int 4 -2,147,483,648 a 2,147,483,647 long long int 8 -(263-1) a 263-1 (Para C99) signed long int 4 Igual que long int unsigned long int 4 0 a 4,294,967,295 unsigned long long int 8 0 a 264-1 (Para C99) float 4 1.17549e–38 a 3.40282e+38 double 8 2.22507e–308 a 1.79769e+308 long double 10 o 12 3.4e–4932 a 1.2e+4932 limits.h Librería que contiene parámetros de entorno, información sobre limitaciones y rangos para tipos enteros. Constante Significado CHAR_BIT Número de bits del tipo char CHAR_MIN Valor mínimo del tipo char CHAR_MAX Valor máximo del tipo char INT_MIN Valor mínimo del tipo int INT_MAX Valor máximo del tipo int LONG_MIN Valor mínimo del tipo long LONG_MAX Valor máximo del tipo long SCHAR_MIN Valor mínimo del tipo char con signo SCHAR_MAX Valor máximo del tipo char con signo SHRT_MIN Valor mínimo del tipo short SHRT_MAX Valor máximo del tipo short UCHAR_MAX Valor máximo de unsigned char USHRT_MAX Valor máximo unsigned short acultad de Inge UINT_MAX Valor máximo unsigned int ULONG_MAX Valor máximo unsigned long 2.6 Variables Una variable es una posición de memoria con nombre que se usa para mantener un valor que puede ser modificado por el programa. Todas las variables han de estar declaradas antes de poder ser utilizadas. La forma general de la declaración es: tipo lista_de_variables; tipo - debe ser un tipo de datos válido con algún modificador. lista_de_variables - puede consistir en uno o más nombres de identificadores separados por comas. 2.6.1 Variables locales Las variables locales son aquellas que se declaran dentro de una función. Este tipo de variables pueden ser utilizadas sólo en las instrucciones que estén dentro del bloque en el que han sido declaradas. Un bloque de código comienza con una llave de apertura y termina con una llave de cierre. Las variables locales sólo existen mientras se está ejecutando el bloque de código en el que son declaradas. O sea, la variable local se crea al entrar en el bloque y se destruye al salir de él. Más aún, una variable declarada dentro de un bloque de código no tiene que ver con otra variable con el mismo nombre que se haya declarado en otro bloque de código distinto. Ejemplo: Variables locales #include<stdio.h> int main() { /*Declaración de una variable local*/ int x=10; if(x == 10) { /*Declaración de una variable local. Pierde su valor cuando sale de este bloque*/ acultad de Inge int x; x=99; printf("x interna: %d\n",x); } printf("x externa: %d\n",x); getchar(); return 0; } 2.6.2 Variables globales Las variables globales se conocen a lo largo de todo el programa y se pueden usar en cualquier parte del código. Además, mantienen sus valores durante toda la ejecución del programa. Las variables globales se crean al declararlas en cualquier parte fuera de la función. Pueden ser accedidas por cualquier expresión, independientemente de la función en la que se encuentre la expresión. Si una variable global y una variable local tienen el mismo nombre, todas las referencias a ese nombre de variable dentro de la función donde se ha declarado la variable local se refieren a esa variable local y no tienen efecto sobre la variable global. Ejemplo: Variables globales #include<stdio.h> //Declaracion de la variable global int cuenta; //Prototipo de la funcion void funcion1(void); int main(void) { //Inicializa la variable global cuenta = 100; //Impresión de la variable global printf("Variable global cuenta: %d\n",cuenta); //Llamada a la función "funcion1()" funcion1(); //Impresión de la variable global printf("Variable global cuenta: %d\n",cuenta); getchar(); return 0; acultad de Inge } //Creación de una función void funcion1(void) { //Declaracion de una variable local int cuenta = 20; printf("Variable local cuenta: %d\n", cuenta); } 2.6.3 Inicialización de variables Colocando el signo igual y una constante después del nombre de un variable se puede dar a la mayoría de las variables un valor a la vez que se declaran. La forma general de inicialización es: tipo nombre_de_variable = valor; Ejemplo: Inicialización de variables #include<stdio.h> int main() { //Declaración e inicialización de variables char c = '&', c2; int y, x = 10; double d = 12.7655, d2; float f=2, f2; char cad[] = "Hola a todos"; //Impresión de las variables printf("Valor del caracter c: %c y c2: %c\n",c,c2); printf("Valor del entero x: %d e y: %d\n",x,y*2); printf("Valor del numero real f: %g y f2: %g\n",f,f2); printf("Valor del numero real d: %g y d2: %g\n",d,d2); printf("Valor del arreglo cad: %s\n",cad); getchar(); return 0; } #include #include #include #include <stdio.h> <limits.h> <float.h> <stdlib.h> int main() acultad de Inge { //Declaracion de variables char a=1; unsigned char e; int c, r1=33000; unsigned int r=33000; unsigned int g; short int b; unsigned short int f; long int d; unsigned long int h; float i; double j; long double k; //Impresion del tama¤o de las variables en bytes printf("Longitud de cada uno de los tipos basicos (bytes)\n\n"); printf("La longitud de char a= %d\n",sizeof(a)); printf("La longitud de unsigned char e= %d\n",sizeof(e)); printf("La longitud de int c= %d\n",sizeof(c)); printf("La longitud de unsigned int g= %d\n",sizeof(g)); printf("La longitud de short int b= %d\n",sizeof(b)); printf("La longitud de unsigned short int f= %d\n",sizeof(f)); printf("La longitud de long int d= %ld\n",sizeof(d)); printf("La longitud de unsigned long int: %d\n",sizeof(h)); printf("La longitud de float: %d\n",sizeof(i)); printf("La longitud de double: %d\n",sizeof(j)); printf("La longitud de long double: %d\n",sizeof(k)); //Impresion del rango de algunos tipos de datos printf("\nValores minimos y maximos de cada uno de los tipos\n\n"); printf("Valores minimo y maximo de char: %d a %d\n",CHAR_MIN,CHAR_MAX); printf("Valor maximo de unsigned char: %u\n",UCHAR_MAX); printf("Valor minimo y maximo de int: %d a %d\n",INT_MIN,INT_MAX); printf("Valor maximo de unsigned int: %u\n",UINT_MAX); printf("Valor minimo y maximo de short int: %d a %d\n",SHRT_MIN,SHRT_MAX); printf("Valor maximo de unsigned short int: %u\n",USHRT_MAX); printf("Valor minimo y maximo de long int: %d a %d\n",LONG_MIN,LONG_MAX); printf("Valor maximo de unsigned long int: %u\n",ULONG_MAX); printf("Valor minimo y maximo de float: %g a %g\n",FLT_MIN,FLT_MAX); printf("Valor minimo y maximo de double: %g a %g\n",DBL_MIN,DBL_MAX); getchar(); return (EXIT_SUCCESS); } 2.6.4 Constantes Las constantes refieren valores fijos que no pueden ser modificados por el programa. Pueden ser de cualquier tipo de datos básico. Para marcar que queremos acultad de Inge que una variable sea constante se utiliza la palabra reservada const y por convención el nombre de la constante se escribe en mayúsculas. const tipo NOMBRE_CONSTANTE = valor; Ejemplo: Constantes #include<stdio.h> /*Prototito de la función*/ void funcion1(void); /*Declaración de una constante global*/ const float PI=3.1415; int main() { const float PI = 3.1416; printf("Constante local (dentro del main): %f\n",PI); /*La expresión de abajo no es válida debido*/ /*a que a una constante no se le puede cambiar su valor*/ /*PI = 3.1415;*/ if(4) { const float PI= 3.14; printf("Constante local (dentro del if): %f\n",PI); } /*Llamada a la función*/ funcion1(); printf("Constante local (dentro del main): %f\n",PI); system("PAUSE"); } void funcion1(void) { /*Impresión de la constante global*/ printf("Constante Global PI = %f\n",PI); } 2.6.5 Constantes de carácter de barra invertida Código Significado \b Espacio atrás \f Salto de página acultad de Inge \n Salto de línea \r Retorno de carro \t Tabulación horizontal \” Comillas dobles \’ Comilla simple \\ Barra invertida \v Tabulador vertical \a Alerta \? Signo de interrogación \0N Constante octal (donde N es una constante octal) \xN Constante hexadecimal (donde N es una constante hexadecimal) acultad de Inge CAPÍTULO 3: OPERADORES Y EXPRESIONES C es un lenguaje muy rico en operadores incorporados. De hecho, dota de un mayor significado a los operadores que la mayoría de los demás lenguajes de computadora. Hay cuatro clases principales de operadores: aritméticos, relacionales, lógicos y a nivel de bits 3.1 Operadores aritméticos Operador Acción - Resta, además de menos monario + Suma * Multiplicación / División % Módulo -- Decremento ++ Incremento 3.2 Operadores relacionales y lógicos En C, verdadero es cualquier valor distinto de cero. Falso es cero. Las expresiones que utilizan los operadores relacionales y lógicos devuelven 1 para cierto y 0 para falso. Operadores relacionales Operador acultad de Inge Acción > Mayor que >= Mayor o igual que < Menor que <= Menor o igual que == Igual != Distinto Operadores lógicos Operador Acción && Y || O ! NO Tabla de verdad p q p && q p || q !p p^q 0 0 0 0 1 0 0 1 0 1 1 1 1 0 0 1 0 1 1 1 1 1 0 0 3.3 Operadores a nivel de bits Dado que C se diseñó para sustituir al lenguaje ensamblador en muchas tareas de programación, era importante permitir todas las operaciones que se pueden realizar en ensamblador. Las operaciones a nivel de bits se refieren a la comprobación, asignación o desplazamiento de los bits reales que componen un byte o una palabra, que corresponden a los tipos estándar char e int con sus variantes. Estas operaciones no se pueden usar sobre float, double, long, double, void u otros tipos más complejos. Operador acultad de Inge Acción & Y | O ^ O exclusiva(XOR) ~ Complemento a uno >> Desplazamiento a la derecha >> Desplazamiento a la izquierda 3.4 Precedencia de los operadores de C Mayor () [] -> . ! ~ ++ -- - (tipo) * & sizeof * / % + << >> < <= > >= == != & ^ | && || ?: = += -= *= /= Menor , acultad de Inge CAPÍTULO 4: INSTRUCCIONES DE CONTROL 4.1 Instrucciones de selección IF La forma general de la instrucción if es: if(expresión) { instrucciones; } else { instrucciones; } donde la cláusula else es opcional. Si la expresión es cierta (cualquier valor que no sea 0), se ejecuta la instrucción o el bloque de instrucciones que constituye el objetivo del if; en caso contrario se ejecuta la instrucción o el bloque de instrucciones que constituyen el objetivo de else, si existe. Sólo se ejecuta el código asociado al if o al else, nunca ambos. SWITCH C incorpora una instrucción de selección múltiple, denominada switch, que compara sucesivamente el valor de una expresión con una lista de constantes enteras o de caracteres. Cuando se encuentra una correspondencia, se ejecutan las instrucciones asociadas con la constante. La forma general de la instrucción switch es: switch(expresion) { case constante1: secuencia de instrucciones break; case constante2: secuencia de instrucciones break; case constante3: secuencia de instrucciones acultad de Inge break; . . . default: secuencia de instrucciones } La expresión debe dar como resultado un valor entero. Por tanto, se pueden utilizar valores enteros o de carácter, mientras que las expresiones reales en coma flotante, no están permitidas. Se comprueba el valor de la expresión, por orden, con los valores de las constantes especificadas en las instrucciones case. Cuando se encuentra una correspondencia, se ejecuta la secuencia de instrucciones asociada con ese case, hasta que se encuentra la instrucción break o el final de la instrucción switch. La instrucción default se ejecuta si no se ha encontrado ninguna correspondencia. La instrucción default es opcional y si no está presente no se ejecuta ninguna acción al fallar todas las comprobaciones. La instrucción break es una de las instrucciones de salto de C. Se puede usar en bucles además de en la instrucción switch. Cuando se encuentra un break en una instrucción switch, la ejecución del programa salta a la línea de código que sigue a la instrucción switch. Hay tres cosas importantes que se deben saber sobre la instrucción switch: La instrucción switch se diferencia de la instrucción if en que switch sólo puede comprobar la igualdad, mientras que if puede evaluar expresiones relacionales o lógicas. No puede hacer dos constantes case en el mismo switch que tengan los mismos valores. Si se utilizan constantes de tipo carácter en la instrucción switch, se convierten automáticamente a sus valores enteros. 4.2 Instrucciones de iteración FOR acultad de Inge El formato general del bucle for de C se encuentra de una forma u otra en todos los lenguajes de programación procedimentales. En C, sin embargo, proporciona una potencia y flexibilidad sorprendentes. La forma general para la instrucción for es: for(inicializacion(es);condicion(es);incremento(s)) { instrucciones; } El bucle for admite muchas variantes. Sin embargo, la inicialización normalmente es una instrucción de asignación que se utiliza para iniciar la variable de control del bucle. La condición es una expresión relacional que determina cuándo finaliza el bucle. El incremento define cómo cambia la variable de control cada vez que se repite el bucle. Estas tres secciones principales deben estar separadas por punto y coma. El bucle for continúa ejecutándose mientras que la condición sea cierta. Una vez que la condición se hace falsa, la ejecución del programa continúa con la instrucción inmediata siguiente saliendo del for. En los bucles for, la prueba de la condición se hace siempre al principio del bucle. Esto supone que el código dentro del bucle puede no ejecutarse si la condición es falsa al comienzo WHILE El segundo bucle disponible en C es el bucle while. Su forma general es: [inicializar] while(expresión) { instrucciones; [incrementos] } La condición puede ser cualquier expresión y cualquier valor distinto de 0 es cierto. El bucle itera mientras la condición es cierta. Cuando la condición se hace falta, el control del programa pasa a la línea inmediatamente siguiente al código del bucle. acultad de Inge DO WHILE A diferencia de los bucles for y while, que analizan la condición del bucle al principio, el bucle do-while comprueba la condición al final. Esto significa que el bucle do while siempre se ejecuta al menos una vez. La forma general del bucle do while es: [inicialización] do { instrucciones; [incrementos] } while(expresión); Aunque las llaves no son necesarias cuando sólo hay una instrucción, se utilizan normalmente para evitar confusiones con el while. El bucle itera hasta que la condición se hace falsa. 4.3 Instrucciones de salto C tiene cuatro instrucciones que llevan a cabo un salto incondicional: return, goto, break y continue. De ellas se pueden usar return y goto en cualquier parte dentro de una función. Las instrucciones break y continue se usan junto con instrucciones de bucle y además también se puede usar break junto con switch. return La función return se usa para volver de una función. Se trata de una instrucción de salto porque hace que la ejecución vuelva al punto en que se hizo la llamada a la función. Un return puede tener o no un valor asociado. Sólo se puede utilizar un return con un valor asociado en una función con valor de vuelta de tipo distinto de void. En ese caso, el valor asociado con return es el valor de vuelta de la función. Un return sin valor de vuelta se utiliza para volver de las funciones de tipo void. La forma general de la instrucción return es: return expresión; acultad de Inge La expresión estará presente sólo si se ha declarado la función como devolviendo un valor. En este caso, el valor de la expresión se convertirá en el valor devuelto por la función. Se pueden usar tantas instrucciones return como se quiera en una función. Sin embargo, la función termina tan pronto como encuentra el primer return. La llave } que termina el código de la función también hace que se vuelva de ella. Equivale a un return sin valor específico. Si esto ocurre en una función declarada de un tipo distinto de void, entonces el valor de vuelta de la función queda indeterminado. Una función declarada como void no puede contener una instrucción return que especifique un valor. Dado que las funciones void no devuelven valor, parece lógico que no contengan ninguna instrucción return que devuelva un valor. break La instrucción break tiene dos usos. Se puede usar para finalizar un case en una instrucción switch. También se puede usar para forzar la terminación inmediata de un bucle, saltando la evaluación condicional normal del ciclo. Cuando se encuentra la instrucción break dentro de un bucle, el bucle finaliza inmediatamente y el control pasa a la instrucción que sigue al bucle. continue La instrucción continue funciona de una forma algo similar al break. Sin embargo, en vez de forzar la terminación, continue fuerza una iteración del bucle y salta cualquier código que exista entre medias. Para el bucle for, continue hace que se ejecuten las partes de prueba condicional y de incremento del bucle. Para los bucles while y do-while, el control del programa pasa a la prueba condicional. acultad de Inge CAPÍTULO 5: FUNCIONES Las funciones son bloques constructores de C y el lugar donde se produce toda la actividad del programa. Las funciones contienen varias sentencias bajo un solo nombre, que un programa puede utilizar una o más veces para ejecutar dichas sentencias. Las funciones ahorran espacio, reduciendo repeticiones y haciendo más fácil la programación, proporcionando un medio de dividir un proyecto grande en módulos pequeños más manejables. La división del código en funciones hace que las mismas se puedan reutilizar en su programa y en otros programas. 5.1 Forma general de una función La forma general de una función es: tipo_dato_retorno nombreFunción(lista de parámetros) { cuerpo de la función return expresión; } El tipo de dato de retorno especifica el tipo de dato que devuelve la función. Una función puede devolver cualquier tipo de dato excepto un array. La lista de parámetros es una lista de nombres separados por comas con sus tipos asociados. Los parámetros reciben los valores de los argumentos cuando se llama a la función. Una función puede no tener parámetros, en cuyo caso la lista de parámetros vacía se puede especificar explícitamente como tal colocando la palabra clave void entre los paréntesis. En las declaraciones de variables se pueden declarar múltiples variables del mismo tipo mediante una lista con los nombres de las variables separadas por comas. En cambio, en las funciones todos los parámetros deben declararse individualmente, incluyendo para cada uno tanto el tipo como el nombre. f(tipo var1, tipo var2, …, tipo varN) acultad de Inge 5.2 Prototipos de las funciones La declaración de una función se denomina prototipo. Específicamente un prototipo consta de los siguientes elementos: nombre de la función, una lista de parámetros encerados entre paréntesis y un punto y coma. En C no es obligatorio incluir el prototipo aunque sí es recomendable para que el compilador pueda hacer chequeos en las llamadas a funciones. Los prototipos se sitúan normalmente al principio de un programa, antes de la definición de la función main(). Cuando se usan prototipos, el compilador puede encontrar e informar sobre conversiones de tipos ilegales entre el tipo de los argumentos usados en la llamada a la función y las definiciones de tipos de sus parámetros. El compilador también detectará diferencias entre el número de argumentos usados en la llamada a la función y el número de parámetros de la misma. 5.3 Parámetros de una función C siempre utiliza el método de parámetros por valor para pasar variables a funciones. Para que una función devuelva un valor a través de un argumento hay que pasar la dirección de la variable, y que el argumento correspondiente de la función sea un puntero; es la forma de conseguir en C un paso de parámetros por referencia. C permite utilizar punteros para implementar parámetros por referencia, ya que por defecto, en C el paso de parámetros es por valor. Los parámetros por valor reciben copias de los valores de los argumentos que se les pasan. La asignación a parámetros por valor de una función no cambia el valor del argumento original pasado a los parámetros. Los parámetros por referencia (declarados con *, punteros) reciben la dirección de los argumentos pasados; a estos les debe preceder el operador &, excepto los arrays. En una función, las asignaciones a parámetros por referencia (punteros) cambian los valores de los argumentos originales. Con el objeto de añadir seguridad adicional a las funciones, se puede añadir a la descripción de un parámetro la palabra const, que indica al compilador que son sólo para pasar información al interior de la función. Si se intenta modificar este parámetro se producirá un mensaje de error. acultad de Inge Ejemplos: Funciones /* Calcula el factorial por medio de una función */ #include<stdio.h> /*Prototipo de la función*/ float factorial(int numero); int main(void) { int num; printf("Dame un numero para calcular el factorial: "); scanf("%d",&num); printf("\nEl factorial de %d es: %.1f",num,factorial(num)); fflush(stdin); getchar(); } /* Función que calcula el factorial*/ float factorial(int numero) { int i; float factorial = 1; for(i=1;i <= numero;i++) factorial = factorial * i; return factorial; } /* Función que identifica si un número es primo Hace la suma de los primos comprendidos entre 1 y 100 */ #include<stdio.h> /*Prototipo de la función*/ char primo(int numero); int main() { int i, suma=0; for(i=1;i <= 100;i++) if(primo(i) == 's') suma = suma + i; acultad de Inge printf("La suma de los números primos comprendidos\n"); printf("entre 1 y 100 es: %d",suma); getchar(); } /*Función que identifica si un número es o no primo*/ char primo(int numero) { int i; for(i=2;i < numero;i++) if(!(numero%i)) return 'n'; return 's'; } /* Suma el valor ascii de los elementos de un arreglo de caracteres */ #include<stdio.h> /*Prototipo de la función*/ int sumaArreglo(char arr[]); int main() { char cadena[] = "Hola a todos"; printf("La suma de los %d",sumaArreglo(cadena)); respectivos ascii getchar(); } /*Función que suma los elementos de un arreglo*/ int sumaArreglo(char arr[]) { int i, sumaAscii = 0; for(i=0;arr[i] != '\0';i++) sumaAscii = sumaAscii + arr[i]; return sumaAscii; } /* Convierte cadena a mayúsculas */ #include<stdio.h> #include<ctype.h> acultad de Inge de la cadena es: /*Prototipo de la función*/ char *cadenaUpper(char *cadena); int main() { char *apuntadorCadena, cad[] = "Hola, como estan?"; int *ap; apuntadorCadena = cadenaUpper(cad); printf("%s",apuntadorCadena); getchar(); } /* Función que convierte cadena a mayúsculas Paso de parámetros por referencia (apuntadores) La función regresa un apuntador a char */ char *cadenaUpper(char *cadena) { int i; for(i=0;*(cadena+i) != '\0';i++) if(!isupper(*(cadena+i))) *(cadena+i) = toupper(*(cadena+i)); return cadena; } 5.4 Argumentos de main() Algunas veces resulta útil pasar información al programa cuando se ejecuta. El método general es pasar información a la función main() mediante el uso de argumentos desde la línea comandos. Un argumento de este tipo es la información que sigue al nombre del programa en la línea de comandos del sistema operativo. Por ejemplo, cuando se compila un programa, puede que se escriba algo como lo siguiente: gcc nombrePrograma donde nombrePrograma es el programa que se va a ejecutar. Hay dos argumentos ya incorporados, argc y argv, que se utilizan para argumentos desde la línea de comandos. El parámetro argc contiene el número de argumentos y es acultad de Inge un entero. Siempre vale 1 por lo menos, ya que el nombre del programa cuenta como el primer argumento. El parámetro argv es un puntero a un array de punteros a caracteres. Cada elemento del array apunta a un argumento de línea de comandos. Todos los argumentos son cadenas; si se necesita un valor numérico, tendrá que ser convertido manualmente por el programa. int main(int argc, char *argv[]) Ejemplo: Argumentos al main #include<stdio.h> int main(int argc, char *argv[]) { printf("Valor de argc: %d\n\n",argc); while(argc != 0) { printf("Argumento argv[%d]: %s\n",argc-1,argv[argc-1]); argc--; } getchar(); return 0; } El programa anterior tiene el nombre de argumentosMain.c. Desde la línea de comandos se compila de la siguiente manera: gcc argumentosMain.c –o argumentosMain y se ejecuta como: argumentosMain uno dos tres acultad de Inge En donde uno, dos y tres son las cadenas pasadas al programa desde la línea de comandos. acultad de Inge CAPÍTULO 6: ARRAYS Y CADENAS Un array es una colección de variables del mismo tipo que se referencian por un nombre común. A un elemento específico de un array se accede mediante un índice. En C, todos los arrays constan de posiciones de memoria contiguas. La dirección más baja corresponde al primer elemento y la dirección más alta al último elemento. Los arrays pueden tener de una a varias dimensiones. El array más común es la cadena, que simplemente es un array de caracteres terminado por un nulo. 6.1 Arrays unidimensionales La forma general de declaración de un array unidimensional es: tipo nombre_variable[tamaño]; Como en otros lenguajes, los arrays tienen que declararse explícitamente para que así el compilador pueda reservar espacio de memoria para ellos. Aquí tipo declara el tipo base del array, que es el tipo de cada elemento del array. El valor de tamaño indica cuantos elementos mantendrá el array. Ej. char cadena[10]; int n[5]; Un elemento se puede acceder indexando el nombre del array. Esto se hace colocando el índice del elemento entre corchetes justo detrás del nombre del array. Ej. cadena[2] = ‘s’; // Asigna el valor de ‘s’ al elemento 2 del arreglo. En C, todos los arrays tienen el 0 como índice de su primer elemento. acultad de Inge 6.2 Almacenamiento en memoria de los arrays Los elementos de los arrays se almacenan en bloques contiguos de memoria. Se pueden referenciar elementos del array utilizando formulas o expresiones enteras para los subíndices. Ejemplo: Si a es un array de números reales y si a[0] ocupa la dirección x, el elemento a[i] ocupa la dirección de memoria x+(i-1)*4 6.3 Tamaño de arrays El operador sizeof devuelve el número de bytes necesarios para contener su argumento. Si se usa sizeof para solicitar el tamaño de un array, esta función devuelve el número de bytes reservados para el array completo. Si se conoce el tipo de dato almacenado en el array y su tamaño, tenemos que la longitud del array, mediante el cociente sizeof/tamaño (dato). Ejemplo: #include <stdio.h> int main() { char c; /* crea el dato de tipo char */ short s; /* crea el dato de tipo short */ int i; /* crea el dato de tipo int */ long l; /* crea el dato de tipo long */ float f; /* crea el dato de tipo float */ double d; /* crea el dato de tipo double */ long double ld; /* crea el dato de tipo long double */ int arreglo[ 20 ]; /* crea el arreglo de 20 elementos int */ int *ptr = arreglo; /* crea el apuntador al arreglo int */ printf("\n char = %d \n", sizeof (c)); printf("\n short = %d \n", sizeof (s)); printf("\n int = %d \n", sizeof (i)); acultad de Inge printf("\n long = %d \n", sizeof (l)); printf("\n float = %d \n", sizeof (f)); printf("\n double = %d \n", sizeof (d)); printf("\n long double = %d \n", sizeof (ld)); printf("\n arreglo = %d \n", sizeof (arreglo)); printf("\n puntero = %d \n", sizeof (ptr)); getch(); return 0; } 6.4 Inicialización de un array Se pueden asignar valores a los arrays antes de ser utilizados. Para asignarlos a cada elemento del array de enteros p, se hace lo siguiente: p[0] = 10; p[1] = 20; p[2] = 30; p[3] = 40; p[4] = 50; Sin embargo, este método no es practico cuando el array contiene muchos elementos, se utiliza normalmente inicializar el array completo en una sola sentencia. Cuando se inicializa un array el tamaño del array se puede determinar automáticamente por las constantes de inicialización. Estas constantes se separan por comas y se encierran entre llaves. Ejemplo: int num [6] = {10, 20, 30, 40, 50, 60}; int x [ ] = {1, 2, 3}; /* Declara un array de 3 elementos */ char ch [ ] = {‘P’, ‘A’, ‘Y’, ‘M’, ‘N’}; /* Declara un array de 5 elementos*/ acultad de Inge 6.5 Cadenas El uso más común de los arrays unidimensionales, con mucho, es como cadenas de caracteres. En C, una cadena es un array de caracteres terminado en nulo. Por tanto, una cadena contiene los caracteres que la conforman seguidos de un nulo. La cadena terminada en nulo es el único tipo de cadena definido en C. C incluye una gran variedad de funciones de manipulación de cadenas. Algunas de estas son: Nombre strcpy(c1,c2) Función Copia c2 en c1 char *strcpy(char *s1, const char *s2); strcat(c1,c2) Concatena c2 al final de c1 char *strcat(char*s1, const char *s2); strlen(c1) Devuelve la longitud de c1 size_t strlen(const char *s); strcmp(c1,c2) Devuelve 0 si c1 y c2 son iguales; menor que 0 si c1 < c2; mayor que 0 si c1 > c2. int strcmp(const char *s1, const char *s2); strchr(c1,car) Devuelve un puntero a la primera ocurrencia de car en c1 char *strchr(char *s, int c); strstr(c1,c2) Devuelve un puntero a la primera ocurrencia de c2 en c1. char *strstr(const char *s1, const char *s2); Estas funciones usan el archivo de cabecera estándar <string.h>. 6.6 Arrays bidimensionales acultad de Inge C admite arrays multidimensionales. La forma más simple de un array multidimensional es el array bidimensional. tipo_dato nombre_del_arreglo[r][c]; Ej. int x[3][4]; //Declaración de un arreglo bidimensional de 3 renglones y 4 columnas Los arrays bidimensionales se almacenan en matrices fila-columna, en las que el primer índice indica la fila y el segundo indica la columna. 6.7 Inicialización de arrays bidimensionales Los arrays bidimensionales se pueden inicializar, al igual que los de una dimensión, cuando se declaran. La inicialización consta de una lista de constantes separadas por comas y encerradas entre llaves. Ejemplo: int arreglo [2],[3] = {1, 2, 3, 4, 5, 6}; acultad de Inge CAPÍTULO 7: PUNTEROS 7.1 ¿Qué son los punteros? Un puntero es una variable que contiene una dirección de memoria. Esa dirección es la posición de otro objeto (normalmente otra variable) en memoria. Por lo tanto se dice que si una variable (puntero) contiene la dirección de otra variable la primera variable apunta a la segunda. Los punteros están muy relacionados con los arrays y proporcionan una vía alternativa de acceso a los elementos individuales del array. Adicionalmente debemos de tomar en cuenta que dentro de la memoria de la computadora, cada dato almacenado ocupa una o más celdas contiguas de memoria, es decir, bytes adyacentes. El número de celdas de memoria requeridas para almacenar un dato depende de su tipo. Por ejemplo, un carácter se almacenará normalmente en un byte (8 bits) de memoria; un entero usualmente necesita 4 bytes contiguos al igual que un tipo flotante; y una cantidad en doble precisión puede requerir ocho bytes contiguos. 7.2 Declaración de apuntadores Si una variable va a ser puntero, entonces antes de ser utilizada debe de declararse como tal. Una declaración de puntero consiste en un tipo de dato, un * y el nombre de la variable. Además, debe seguir las convenciones para nombres de variables. La sintaxis general de declaración es: tipo_dato *nombreApuntador; Ejemplos: 1. int *apunEntero; 2. float *apunReal; acultad de Inge 3. char *apunCaracter; El apuntador apunEntero es un apuntador a int, el apuntador apunReal es un apuntador a float y el apuntador apunCaracter es un apuntador a char, el tipo de dato del puntero define el tipo de variables a las que puede apuntar el apuntador. Los operadores de puntero Hay dos operadores de punteros: & y *. & es un operador monario que devuelve la dirección de memoria de su operando. Por ejemplo: int x 10,*apunEntero; apunEntero & x; pone en apunEntero la dirección de memoria en donde está almacenada la variable Por lo tanto se puede pensar en el operador x. & como devolviendo “la dirección de”. Por lo tanto la instrucción de asignación anterior significa “ apunEntero recibe la dirección de x ”. Para entender bien la asignación anterior, supongamos que la variable x utiliza la posición de memoria AFFB para guardar el valor de 10. Entonces, después de la asignación, apunEntero contiene el valor de AFFB. El segundo operador de punteros, *, es el complemento de &. Es un operador monario que devuelve el valor que se encuentra en la dirección que le sigue. Por ejemplo, #include<stdio.h> int main() { int x=2,y=3; int *apunEntero; /*Declaracion de un puntero tipo entero*/ /*Impresion de los valores de las variables*/ acultad de Inge printf("x=%d, y=%d, apunEntero=%p\n\n",x,y,apunEntero); apunEntero = &x; /*Se le asigna la dirección de x al puntero apunEntero*/ /*Imprime los valores de las direcciones de memoria de x y el apuntador. En este caso son iguales*/ printf("Dir x: %p, Dir apuntador: %p\n\n",&x,apunEntero); /*Se le asigna a y el valor localizado en la direccion del apuntador apunEntero */ y = *apunEntero; printf("x=%d, y=%d\n\n",x,y); /*Se almacena el valor de 5 en la direccion de memoria de apunEntero*/ *apunEntero = 5; printf("Lo que hay en la direccion %p: %d\n\n",apunEntero,*apunEntero); system("PAUSE"); } 7.3 Punteros y Arrays Los arrays y los punteros mantienen una estrecha relación en el lenguaje C. El nombre de un array es un puntero, contiene la dirección en memoria de comienzo de la secuencia de los elementos que forma el array. Es un puntero constante ya que no se puede modificar, solo se puede acceder para indexar a los elementos del array. Consideremos el siguiente fragmento como ejemplo: char cad[80], *p1; p1 = cad; A p1 le ha sido asignada la dirección del primer elemento del array cad. Para acceder al quinto elemento de cad se escribe: acultad de Inge cad[4] o bien, *(p1+4); Se puede declarar un array de punteros, como array que contiene como elementos punteros, cada uno de los cuales apuntará a otro dato especifico. Por ejemplo: int *p[10]; en la lineal anterior se reserva un array de diez variables punteros a enteros. 7.4 Punteros de cadenas La siguiente declaración es un array de caracteres que contiene las veintiséis letras del alfabeto internacional. char alfabeto[27] = “ABCDEFGHIJKLMNOPQRSTUVWXYZ”; si p es un puntero a char: p = &alfabeto[0]; /* Se obtiene la dirección de letra A */ p = &alfabeto[4]; /* Se obtiene la dirección de la letra E */ p = &alfabeto[8]; /* Se obtiene la dirección de la letra I */ p = &alfabeto[14]; /* Se obtiene la dirección de la letra O */ p = &alfabeto[20]; /* Se obtiene la dirección de la letra U */ 7.5 Punteros a funciones Es posible también crear punteros que apunten a funciones. En lugar de direccionar datos, los punteros de funciones apuntan a código ejecutable. Al igual que los datos, las funciones se almacenan en memoria y tienen direcciones iniciales. Tales funciones se pueden llamar en un modo indirecto, es decir, mediante un puntero cuyo valor es igual a la dirección inicial de la función en cuestión. acultad de Inge La sintaxis general para la declaración de un puntero a una función es: tipo_de_retorno (*PunteroFuncion) (<lista de parámetros>); Este formato indica al compilador que PunteroFuncion es un puntero a una función que devuelve el tipo Tipo_dato_retorno y tiene una lista de parámetros. Un puntero a una función es un puntero cuyo valor es la dirección del nombre de la función. acultad de Inge CAPÍTULO 8: ESTRUCTURAS 8.1 Definición Una estructura es una colección de variables que se referencia bajo un mismo nombre, proporcionando un medio conveniente de mantener junta una información relacionada. Una declaración de estructura forma una plantilla que puede utilizarse para crear objetos estructuras (ejemplares de una estructura). Las variables que componen a la estructura se llaman miembros de la estructura. La forma general para declarar una estructura es la siguiente: struct nombreEstructura { tipo miembro1; tipo miembro2; tipo miembro3; … … … tipo miembro n-1; tipo miembro n; }[variables de estructura]; La palabra clave struct indica al compilador que se está declarando una estructura. Y la declaración termina con un punto y coma. Para declarar una estructura que represente a una persona sería como sigue: struct persona { char nombre[70]; char direccion[120]; unsigned int edad; float ingresoMensual; char sexo; }; En este momento tenemos una estructura llamada persona, con 5 miembros, pero todavía no se tienen las variables que representen a diferentes personas. Para eso hacemos lo siguiente: acultad de Inge struct persona estudiante, obrero, empresario; Ya hemos declarado tres variables del tipo estructura las cuales son: estudiante, obrero y empresario. Cada variable mantiene “dentro” de ella los 5 miembros de la estructura persona. Por ejemplo, el miembro nombre de estudiante está aparte y es distinto del miembro nombre para obrero y empresario. Lo anterior lo podemos hacer en un solo paso: struct persona { char nombre[70]; char direccion[120]; unsigned int edad; float ingresoMensual; char sexo; } estudiante, obrero, empresario; 8.2 Acceso a los miembros de la estructura Los miembros individuales de la estructura se acceden a través del operador . (denominado usualmente operador punto). Para tener acceso a los miembros de las variables de estructura hacemos lo siguiente: variableDeEstructura.miembro 8.3 Asignación de valores Usando la variable estudiante de la estructura persona: strcpy(estudiante.nombre,”Israel Duran Martinez”); strcpy(estudiante.direccion,”Calle 11 numero 14-A”); estudiante.edad = 32; estudiante.ingresoMensual = 10.21; estudiante.sexo = ‘M’; Usando la variable obrero de la estructura persona: strcpy(obrero.nombre,”Israel Duran Martinez”); strcpy(obrero.direccion,”Calle 11 numero 14-A”); acultad de Inge obrero.edad = 32; obrero.ingresoMensual = 10.21; obrero.sexo = ‘M’; 8.4 Punteros a estructuras C permite punteros a estructuras igual que permite punteros a cualquier otro tipo de variables. Sin embargo, hay algunos aspectos especiales que afectan a los punteros a estructuras. Declaración de un puntero a una estructura Al igual que los demás punteros, los punteros a estructuras se declaran escribiendo * delante del nombre de la variable de una estructura. Por ejemplo, asumiendo la estructura dir previamente definida, lo siguiente declara puntero_dir como un puntero a un dato de ese tipo: struct dir *puntero_dir; Uso de punteros a estructuras Existen dos usos principales de los punteros a estructuras: generar un paso por referencia de una estructura a una función y crear listas enlazadas y otras estructuras de datos dinámicas utilizando el sistema de asignación dinámica de memoria. Cuando se pasa a una función un puntero a una estructura, solo se introduce en la pila la dirección de memoria. Esto significa que la llamada a la función es muy rápida. Para encontrar la dirección de una variable de estructura se coloca el operador & antes del nombre de la estructura. acultad de Inge Ejemplo: struct bal { float balance; char nombre[80]; } persona; struct bal *p; /* declaración de un puntero a estructura */ en la siguiente línea se obtiene la dirección de memoria de la estructura persona en el puntero p: p = &persona; En el siguiente ejemplo se puede apreciar claramente el funcionamiento de los operadores en punteros: #include<stdio.h> int main() { int x=10,y; int *punteroEntero; printf("x=%d\n",x); printf("punteroEntero=%p\n",punteroEntero); //Asignar la direccion de x a punteroEntero punteroEntero = &x; y=*punteroEntero; printf("x=%x\n",y); //Imprime la direcion de memoria de x y de punteroEntero printf("punteroEntero=%p\n",punteroEntero); printf("Direccion de x=%p\n",&x); //Imprime el contenido asignado al puntero acultad de Inge *punteroEntero = 15; printf("Contenido del puntero=%d\n",*punteroEntero); printf("x=%d\n",x); getchar(); return 0; } acultad de Inge CAPÍTULO 9: ARCHIVOS En C, un archivo puede ser cualquier cosa, desde un archivo de disco hasta una terminal o una impresora. Mediante una operación de apertura se asocia una secuencia con un archivo específico. Una vez que el archivo está abierto, se puede intercambiar información entre éste y el programa. 9.1 Manejo de Archivos Cada secuencia asociada a un archivo tiene una estructura de control de tipo FILE. La librería en donde se encuentran las funciones para manejar archivos es <stdio.h> Nombre Función fopen( ) Abre un archivo. fclose( ) Cierra un archivo. putc( ) Escribe un carácter en un archivo. fputc( ) Lo mismo que putc(). getc( ) Lee un carácter de un archivo. fgetc( ) Lo mismo que getc(). fgets( ) Lee una cadena de un archivo. fputs( ) Escribe una cadena en un archivo. fseek( ) Localiza un byte específico de un archivo. ftell( ) Devuelve la posición actual en el archivo. fprintf( ) Hace lo mismo que printf( ) solo que sobre el archivo. fscanf( ) Hace lo mismo que scanf( ) solo que sobre el archivo. feof( ) Devuelve verdadero si se ha llegado al final del archivo. ferror( ) Devuelve verdadero si se ha producido algún error. rewind( ) Coloca el indicador de posición del archivo al inicio del mismo. remove( ) Elimina un archivo. fread() Lee de un archivo fwrite() Escribe en un archivo fflush( ) acultad de Inge Vacía un archivo. A continuación se presenta cada función con su prototipo y un pequeño ejemplo. fopen() La función fopen() abre una secuencia para que pueda ser utilizada y vincula un archivo con la misma. Después devuelve el puntero al archivo asociado con ese archivo. La función fopen() tiene este prototipo: FILE *fopen(const char *Archivo, const char *Modo); fclose() La función fclose() cierra una secuencia que haya sido abierta con una llamada a fopen(). Escribe en el archivo toda la información que todavía se encuentre en el búfer del disco y realiza un cierre formal del archivo a nivel sistema operativo. La función fclose() tiene este prototipo: int fclose(FILE *Archivo); putc() Permite escribir un carácter Car en el flujo asociado a archivo. Tiene el siguiente prototipo: int putc(int Car, FILE *ARCHIVO); Ejemplo: putc(Car, Archivo); fputc() Permite escribir un carácter en el flujo asociado a Archivo. Tiene el siguiente prototipo: int fputc(int Car, FILE *ARCHIVO) acultad de Inge Ejemplo: fputc(Car, Archivo); getc() Proporciona el siguiente carácter de flujo de entrada asociado a Archivo, avanzando el apuntador del archivo al siguiente carácter. Si el apuntador ha llegado al final proporciona EOF. Tiene el siguiente prototipo: int getc(FILE *Archivo) Ejemplo: FILE *Archivo; Car=getc(Archivo); while(Car!=EOF); printf(“%c, Car”); Car=getc(Archivo); fgetc() Proporciona el siguiente carácter de flujo de entrada asociado a Archivo, avanzando el apuntador del archivo al siguiente carácter. Si el apuntador ha llegado al final proporciona EOF. Tiene el siguiente prototipo: int fgetc(FILE *Archivo); Ejemplo: FILE *Archivo; Car = fgetc(Archivo); while(Car!=EOF) printf(“%c, Car”); Car=fgetc(Archivo); acultad de Inge fgets() Permite leer hasta Numero-1 caracteres del flujo de entrada asociado a Archivo; y los coloca en el arreglo de caracteres apuntado por Cadena. Archivo y Cadena están cualificados por restrict. Si no tiene éxito devuelve el valor NULL. Tiene el siguiente prototipo: char *fgets(char *Cadena, int Numero, FILE *Archivo) Ejemplo: FILE *Archivo; fgets(Cadena, 20, Archivo) while(Cadena!=NULL) printf("%s",Cadena); fgets(Cadena, 20, Archivo) fputs() Permite escribir el contenido del arreglo de caracteres apuntado por Cadena; en el flujo asociado a Archivo. En C99; Archivo y Cadena están cualificados por restrict. Tiene el siguiente prototipo: int fputs(const char *Cadena, Ejemplo: fputs(Cadena, Archivo); acultad de Inge FILE *Archivo); fseek() Permite colocar el apuntador del archivo asociado al nombre Archivo a cuantos bytes de Origen. Donde Origen puede ser desde el principio del archivo (0 ó SEEK_SET); desde la posición actual (1 ó SEEK_CUR) o desde el final del archivo (2 ó SEEK_END). Tiene el siguiente prototipo: int fseek(FILE *Archivo, long int Cuantos, int Origen); Ejemplo: FILE *pfile; fseek (pfile, 5, SEEK_CUR); ftell() Proporciona el número de byte donde se encuentra el apuntador del archivo asociado al nombre Archivo. Proporciona -1 si hay error. Tiene el siguiente prototipo: l on g i nt ftel l (FILE *Arch i vo); Ej empl o: FILE *Archivo; if( (comienzo=ftell(archivo) ) < 0 ) printf( "ERROR: ftell no ha funcionado" ); else printf( "Posicion del fichero: %d", comienzo ); fprintf() Permite escribir en el archivo asociado al nombre físico Archivo, los elementos indicados por los formatos; mismos que son iguales a los que se usan en la función printf. Tiene el siguiente prototipo: int fprintf(FILE *Archivo, acultad de Inge const char *Formato) fscanf() Permite leer del flujo asociado a Archivo, los elementos indicados por los formatos; mismos que son iguales a los que se usan en la función scanf. Tiene el siguiente prototipo: int fscanf(FILE *Archivo, const char *Formato) ; feof() Proporciona un valor diferente de cero si el apuntador del archivo asociado al flujo Archivo está al final; en caso contrario proporciona el valor cero. Tiene el siguiente prototipo: int feof(FILE *Archivo); Ejemplo: FILE *Archivo; while(!feof(Archivo)) /* Procesa */ ferror() Proporciona el valor cero si no se ha producido error asociado al flujo Archivo, en caso contrario, proporciona un número diferente de cero. Tiene el siguiente prototipo: int ferror(FILE *Archivo); Ejemplo: FILE *Archivo; if(ferror(Archivo)) printf(“Error en el Archivo”); exit(1); acultad de Inge rewind() Permite colocar el apuntador del archivo identificado por Archivo en el byte cero. Tiene el siguiente prototipo: void rewind(FILE *Archivo); Ejemplo: FILE *Archivo; rewind(Archivo); rename() Permite cambiar el nombre del archivo identificado especificado por NuevoArch. Tiene el siguiente prototipo: e(const int rena char *Archivo, t char *NuevoArch); Ejemplo: FILE *Archivo rename(Archivo, NuevoArch); remove() Permi eliminar el archivo identificado por rchivo. ene el siguien e prototipo: i remove(const char *Archivo); acultad de Inge por Archivo al Ejemplo: FILE *Archivo; Remove(Archivo); fread() Permite leer Cuantos elementos, cada uno de Tamaño bytes, del archivo asociado nombre físico Archivo; y los coloca en Datos. Tiene el siguiente proto po: Si _t fread(void Datos, size_Tamaño, siz _t Cuantos, FILE *Archivo); Ejemplo: FILE *fi chero; stru ct agenda registro; fread(®istro,sizeof(registro),1,fichero) fwrite() Permite escribir Cuantos elementos, cada uno de tamaño bytes de Datos el archivo asociado al nombre físico Ar chivo. Tiene el siguiente prototipo: Si _t fwrit (const void *Datos, size_t Tamaño, size_t Cuantos, FILE *Archivo); Ejemplo: FILE *fich acultad de Inge str t agenda registro; fwrite(®istro,sizeof(registro),1,fichero); fflush() Escribe físicame e en Archivo el contenido de búfer de s alida. Tiene el siguiente prototipo: int fflush(FILE *Archivo); Ejemplo: FILE *Archivo; fflush(Archivo); 9.2 El puntero a archivo El puntero a archivo es el hilo conductor que unifica el sistema de E/S de C. Un puntero a archivo es un puntero a una estructura de tipo FILE. Apunta a información que define varias cosas sobre el archivo, incluyendo su nombre, su estado y la posición actual dentro de él. En esencia, el puntero a archivo identifica un archivo específico y es utilizado por la secuencia asociada para dirigir el funcionamiento de las funciones de E/S. Para leer o escribir sobre archivos, los programas tienen que utilizar punteros. Para di oner de una variable acultad de Inge unte ro a arch ivo e utili za una inst cció n com o e ta: FIL E *arc h; Eje mpl : #inc lude <std io.h > #inc acultad de Inge lude std lib .h> int mai n() { //P unt ero s arc hiv o FIL E *le c ura , *es cri tur a; cha r c, lin ea[ 100 acultad de Inge 0]; int i, con t=0 //A bri mos arc hiv os if( (le ctu ra= fop en( " rch ivo Sal ida txt","r")) == NULL) { printf("No se puede abrir el archivo"); getchar(); exit(1); } if((escritura=fopen("copia rchivoSalida acultad de Inge txt","w")) == ULL) { printf("No s p ede abrir el archivo"); getchar ); exit(1); } while(!fe f(lectura)) { //Leemos las line s de arc ivo fgets(linea,1000 lectura); fpr ntf(escritura,"%s", inea); } //Ce r mos archivos fclose(lectura); fclose(escritura); //getchar(); return 0; } acultad de Inge 9.3 Apertura de un archivo La función fopen() abre una secuencia para que pueda ser utilizada y vincula un archivo con la misma. Después devuelve el puntero al archivo asociado con ese archivo. A menu el archivo es un archivo de disco. La función fop () tiene este prototipo: FILE *fopen(const char *nombre, const char *modo); Donde nombre es un puntero a una cadena de caracteres que representa un nombre válido de archivo y puede incluir una especificación de r a. La cadena a la que apunta modo determina de qué forma se abre el archivo. Ejem lo: FILE *pfile; pfile = fopen ( "ejemplo.txt" , "w" ) if(pfile == N LL) { rint ROR, no se puede abrir el archivo"); getchar(); exit(1); } acultad de Inge 9.4 Cierre de un archivo La función fclose( ) cierra una secuencia que haya sido abierta con una llamada a fopen( ). Escribe en el archivo toda la información que todavía se encuentre en el búfer del disco y realiza el cierre formal del archivo a nivel del sistema operativo. Un error en el cierre de una secuencia puede generar todo tipo de problemas, incluyendo la pérdida de datos, destrucción del archivo y otros posibles errores intermitentes en el programa. Como existe un límite en el número de archivos que pueden estar abiertos si ltáneamente, puede re ltar nec sar io cer rar un arc hiv o ant es de abr ir otr o. int fc ose (FI LE acultad de Inge *fp ) Eje m lo: FIL E *pf ile ; pfi le = fop en ( "ej emp lo. txt " , "w" ) if( pfi le == N LL) { rin t ("E acultad de Inge RRO R, no se ede abrir el archivo"); getchar(); exit(1); } fclose (pfile); Modo de apertura de archivos: Nombre Función R A bre un archivo de texto para lectura. W Crea un archivo de texto para escritura. A A bre un arch i o de texto para añadir información. Rb Abre un archivo binario para lectura. w acultad de Inge b Cre a un arch iv binario para escritura. ab Abre un archivo binario para añadir información. r+ A bre un arch ivo de text para lectura/escritura. w+ Crea un archivo de texto para lectura/escritura. Ab e un archivo de texto en modo lectura/escritura para añadir información. r+b Abre un archivo binario para lectura/escritura. w +b Cr ea un arch acultad de Inge ivo bina rio par a lect ura/ escr itur a. a +b A br o crea un archivo binario en modo lectura/escritura para añadir información. ibliograf SCHILDT HERBERT Manual de referencia C España, McGraw-Hill, 2001 709 págs. JOYANES IS Programación en C Libro de Pro lemas España, McGraw-Hill 2004 390 á . acultad de Inge T RIED BYRON Programación en C España ll, 2005 659 págs. acultad de Inge McGraw-H