Está en la página 1de 35

El lenguaje de programacin C o (Explicacin de las transparencias) o

(C) Javier Miranda 1996-2004 Reservados todos los derechos Esta coleccin de notas es un material complementario de la o coleccion de transparencias. Forma parte del contenido del libro Dise o de Software con C y UNIX (Volumen 1), n J.Miranda (1996), con ISBN 84-87526-45-4. 18 de febrero de 2004

18 de febrero de 2004 Notas 1

4.
4.1.

El lenguaje de programacin C o
Historia de C

El nacimiento de C est muy ligado al nacimiento de Unix en los laboratorios Bell. a En 1968 Thompson cre un grupo para investigar alternativas al sistema operativo o Multics y desarroll Unix sobre un computador DEC PDP-7 (8k-18bit palabras de o memoria, sin software disponible). Escribi la versin original de Unix mediante macros o o y un ensamblador cruzado (cargando el ejecutable mediante una cinta). De esta forma desarroll: el n cleo primitivo de Unix, un editor bsico, un ensamblador sencillo que o u a generaba siempre el ejecutable en el chero a.out, un interprete de comandos bsico y a los comandos bsicos del S.O. (rm, cat, cp). A partir de este momento ya pudo escribir a los programas directamente en Unix. En 1969 Thompson intenta utilizar FORTRAN para escribir el S.O., pero lo abandona rpidamente y crea el lenguaje B (simplicacin del lenguaje BCPL). B es un a o lenguaje para programacin de sistemas, peque o, con descripcin compacta, fcilo n o a mente traducible y cercano a la mquina. Sin embargo, Thompson comprob que B no a o era adecuado para reescribir Unix y sus utilidades. En 1970 Thompson continu el desarrollo de Unix sobre DEC PDP-11 (24k), y reeo scribi Unix en ensamblador del PDP-11 utilizando 12k para el n cleo de Unix y 12k o u como disco RAM. En 1971 Unix comenz a tener usuarios (que programaban en ensamblador con rutinas o de biblioteca). De esta forma Steve Johnson cre yacc. Este mismo a o Thompson o n modic B y cre el lenguaje NB (introduciendo bsicamente los tipos de datos int, o o a char, los punteros y los arrays). En 1972 introdujeron en el lenguaje un preprocesador y realizaron peque as modin caciones en la evaluacin de las expresiones e introdujeron nuevos operadores. o En 1973 se cre C, Thompson reescribi Unix en C e instal C en otras arquitecturas o o o (Honeywell 635, IBM 360/370) y Lesk desarroll un mdulo de E/S estndar. o o a En 1978 Kernighan y Ritchie publicaron el libro The C programming language. (Kernighan se encarg de la exposicin de C y Ritchie de la interfaz con Unix y el apndice o o e manual de referencia tcnica de C) y Steve Johnson desarroll pcc (un compilador e o de C fcilmente transportable). a En 1979 Johnson crea lint adaptando pcc. En 1983 se crea el comite X2J11 para la creacin del estndar de C. o a En 1989 C se convierte en estndar ISO/IEC 9899-1990 a A partir de C se han desarrollado los siguientes lenguajes: Objetive C [Cox, 86], C++ [Stroustrup, 86], C Concurrente[Gehani, 89], C* [Thinking, 90], C Concurrente Tolerante a fallos [Cmelik, Gehani, Roome].

18 de febrero de 2004 Notas 2

4.2.

El compilador de C

El compilador de C consta internamente de 4 programas: el preprocesador, el compilador, un ensamblador y un enlazador. El preprocesador se encarga de eliminar los comentarios del programa y de interpretar sus comandos. Tras el preprocesador se ejecuta el compilador, que genera un chero equivalente en ensamblador. Este chero se ensambla para generar el chero objeto (.o). Finalmente el enlazador combina todos los cheros objeto especicados en la l nea de comando (incluyendo los cheros objeto de las bibliotecas) y genera el chero ejecutable.

Ejemplos: 1. Llamar al preprocesador, compilador, ensamblador, enlazador y generar el ejecutable en el chero a.out. cc ejemplo.c 2. Llamar al preprocesador, compilador y ensamblador para generar los cheros objeto asociados a los cheros main.c, getline.c e index.c. A continuacin llamar o al enlazador pasandole los tres cheros objeto para generar el chero ejecutable myprog. cc main.c getline.c index.c -o myprog 3. Llamar al preprocesador, compilador y ensamblador para generar el chero objeto asociado al chero main.c. A continuacin llamar al enlazador pasandole los tres o cheros objeto para generar el chero ejecutable. cc main.c getline.o index.o

18 de febrero de 2004 Notas 3

4.3.

El preprocesador

El preprocesador de C es un procesador de macros simple que conceptualmente procesa el chero fuente de un programa C antes de su compilacin. Se controla mediante l o neas de comandos (l neas del programa fuente C) que pueden ir en cualquier parte del chero fuente y deben cumplir las siguientes reglas: 1. Deben comenzar comienzan con el carcter #. a 2. Cada orden debe ocupar una l nea.

4.3.1.

Macros

El comando #dene introduce una denicin de macro. Una macro asocia un ideno ticador a una cadena (string). El preprocesador sustituye las ocurrencias de dicho identicador por la cadena de reemplazo (sustituyendo los parmetros formales que a aparezcan en el cuerpo de la macro por el valor de los parmetros proporcionado en a el momento de la invocacion de la macro). El ambito de las macros va desde el punto de su denicin hasta el nal del chero. Las macros pueden declararse desde la l o nea comandos (cuando se invoca el compilador). Por ejemplo: cc -DM68000=1 file.c . . . dene la macro M68000 con el valor 1 antes de compilar el chero le.c. El comando #undef borra la denicin de una macro previamente denida. o

18 de febrero de 2004 Notas 4

4.3.2.

Compilacin condicional o

La compilacin condicional se utiliza para incorporar u omitir de forma selectiva una o secuencia de sentencias. Por convenio los cheros que se incluyen tienen el sujo .h. Se utiliza para incluir sentencias de depuracin de los programas C y para aumentar la o portabilidad de los programas.

4.3.3.

Inclusin de cheros o

El comando #include hace que el contenido del chero especicado se procese como si estuviese ubicado en el lugar del comando. Existen dos formas de nombrar el chero, y dieren en donde est almacenado el chero especicado. a #include "fichero.h" /* Busca en el directorio actual */ #include <fichero.h> /* Busca el sitio estandar /usr/include */ Este comando se utiliza generalmente para acceder a grupos de macros. De esta forma, varios cheros ven las mismas constantes. Sin embargo, debe tenerse especial cuidado con los posibles bucles y duplicaciones de macros. La forma correcta de evitar estos problemas consiste en utilizar el comando #ifndef de la siguiente forma: #ifndef MAX #define MAX 50 /* Resto del fichero de definiciones */ #endif

18 de febrero de 2004 Notas 5

4.4.

Introduccin a C o

Veamos con un ejemplo algunas de las caracter sticas del lenguaje C: 1. C es un lenguaje de formato libre (excepto los comandos del preprocesador). 2. Los comentarios pueden ponerse en cualquier sitio donde pueda ponerse un espacio en blanco. Comienzan mediante /* y terminan con */. 3. Es un lenguaje sensible a may sculas y min sculas. Por convenio se suele prograu u mar utilizando MAYUSCULAS para las CONSTANTES y min sculas para las u variables. 4. Los identicadores de C estan formados por una secuencia de letras, digitos y . El primer carcter debe ser una letra o . a 5. Las palabras reservadas de C son: PALABRAS CLAVE (en minuscula) ----------------------------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 - Algunas implementaciones tambien reservan fortran y asm. - const, signed y volatile son palabras nuevas de C-ANSI - enum y void son nuevas con respecto a la primera version de C. 6. Los programas C se estructuran mediante funciones. Los parmetros se pasan siempre por valor. a La funcin main es necesaria en todo programa (ya que es la primera en o ejecutarse) y puede tener parmetros (los veremos en la seccin 4.13.5). a o 7. Las variables pueden inicializarse en la declaracin. o 8. La nalizacin del programa ocurre cuando: o Se naliza main. Se ejecuta una sentencia exit(). Ocurre una interrupcin del programa (externa, interna). o

18 de febrero de 2004 Notas 6

4.5.

Tipos de datos

C tiene cuatro tipos de datos bsicos: carcter (char), entero (int), otante de simple a a precisin (oat) y otante de doble precisin (double). Adems tiene tres cualicadores o o a que pueden aplicarse a los tipos bsicos para ajustar la precisin de los tipos bsicos: a o a short, long y unsigned (aritmtica mdulo 2N , siendo N el n mero de bits). Los rangos e o u estn denidos por la implementacin y son especicados en el chero <limits.h>. Si a o se utiliza un cualicador (short, long, unsigned) y se omite el tipo, se presupone que la variable es de tipo int. long int factorial; unsigned int natural; signed char c; /* -127..127 */ short int dia,mes; 4.6. Constantes Constantes tipo carcter. a ascii \{A} octal \101 hex \x41

Constantes tipo string. Son una secuencia de caracteres entre comillas dobles. Las strings adyacentes son automticamente concatenadas en una unica string. a C utiliza el carcter null (\0) como terminador de strings. a Constantes tipo entero. El tipo de una constante entera depende de su forma, valor y sujo1 . decimal octal (comienzan por 0) hex 1234 015 0x4f 12L 015L 0x4f3 12U 015U

Constantes tipo oat. Su sintaxis es: int.fraccin [ E [+|-]int[F|L], signicando o los sujos F y L oat y long double respectivamente. Puede omitirse: La parte entera o la parte fraccionaria (pero no ambas). El punto decimal o el exponente (no ambos).

Si no tiene sujo adopta el primero de los siguientes tipos: si es decimal adopta int, long int, unsigned long int; si es octal o hex adopta int, unsigned int, long int, unsigned long int.

18 de febrero de 2004 Notas 7

4.7.

Operadores y expresiones Aritmticos: Debe tenerse en cuenta que la divisin entera trunca, y que el resto e o (operador mdulo) adopta el mismo signo que el dividendo. Toda la aritmtica o e entera se realiza al menos con rango int (short int es convertido automticamente a en int, lo que se denomina promocin integral). En expresiones mixtas se realiza o conversin automtica al mayor tama o. o a n Relacionales. Adoptan los valores 0 para falso y 1 para verdadero. Lgicos. o Autoincremento, autodecremento. Existen cuatro posibilidades: ++variable; Preincremento; --variable: Predecremento variable++: Postincremento; variable--: Postdecremento Cuidado con los efectos laterales. Asignacin. Existen dos tipos de asignacin: o o 1. Asignacin m ltiple. Operador asociativo por la izquierda que realiza cono u versiones de tipo impl citas. a=b=c+d ===> a=(b=(c+d)) 2. Asignacin compuesta (a op= b). Puede utilizarse con: o Cualquiera de los operadores aritmticos. e Todos los operadores de nivel de bit excepto el complemento a 1. La asignacin compuesta tiene las siguientes ventajas: o a) Concisa. b) Se corresponde mejor con la forma en que piensa la gente. Decimos suma 2 a i o incrementa i en 2, no toma i, smale 2 y vuelve a colocar el u resultado en i. c) En expresiones complicadas hace el cdigo ms fcil de comprender, ya o a a que el lector no necesita comprobar si dos expresiones complicadas son realmente la misma. Por ejemplo: yyval[yypv[p3+p4] + yypv[p1+p2]] += 2 d ) Puede ayudar al compilador a producir un cdigo ms eciente. o a Expresin condicional: Eval a la expresin e1. Si es cierto, eval a la expresin e2 o u o u o y toma el resultado como el valor de la expresin condicional; si es falso, eval a o u la expresin e3 y toma este resultado como el valor de la expresin condicional. o o Operador coma: Eval a la primera expresin (comenzando por la izquierda) y u o descarta el resultado; a continuacin eval a la siguiente expresin y toma este o u o valor como el resultado de la expresin. o Operador sizeof(): Operador unitario que devuelve un entero igual al tama o en n bytes de cualquier objeto.

18 de febrero de 2004 Notas 8

Precedencia de operadores (ver tabla). Puede obviarse si no se evitan los parntee sis. Conversin de tipos. Se puede forzar la conversin expl o o cita del tipo coaccionadade una expresin mediante una construccin denominada cast. El operador cast tiene o o la misma precedencia que cualquier otro operador unitario2 .

4.7.1.

Orden de evaluacin de expresiones o

C no especica en que orden se eval an los operandos de un operador. Por ejemplo, u en una sentencia como x = f ()+g(), f puede ser evaluado antes que g o viceversa. Entonces, si f o g alteran alguna variable externa, de la cual depende la otra funcin, x puede depender del orden de evaluacin. o o Tampoco especica el orden de evaluacin de los argumentos de una funcin esta o o especicado. Asi, la sentencia: printf ("%d %d\n", ++n, f(n) ); puede producir diferentes resultados en distintas mquinas, seg n que n sea ina u crementado antes o despues de la llamada a f (). La solucin consiste en escribir o ++n; printf ("%d %d\n", n, f(n) );

2 Existe un punto oscuro en la conversin de caracteres a enteros. El lenguaje no especica si las variables o de tipo char tienen signo o no. De esta forma, dependiendo de las caracter sticas de la arquitectura de cada mquina puede producir un entero negativo o no. a

18 de febrero de 2004 Notas 9

4.8.

Control de ujo

Todas las sentencias de control de ujo de C estan asociadas a una unica sentencia. Si se desea asociarlas a un bloque debe crearse un nuevo ambito (mediante { }). Sentencias condicionales 1. IF: El resultado de evaluar la expresin logica es un entero donde cualquier o valor diferente de 0 se interpreta como verdadero. Se asocia else al if ms a interno. 2. SWITCH: Todas las alternativas deben ser diferentes. Debe combinarse con la sentencia BREAK. Sentencias iterativas 1. WHILE. 2. DO-WHILE. 3. FOR. for (expr1; expr2; expr3) sentencia; === expr1; while (expr2) { sentencia; expr3; }

Puede omitirse cualquiera de las expresiones. Sentencias de bifurcacin o 1. BREAK: Sale de la sentencia SWITCH, WHILE o FOR ms interna. a 2. CONTINUE: Salta a reevaluar la condicin del bucle ms interno. o a 3. GOTO: Salto incondicional.

18 de febrero de 2004 Notas 10

4.9.

Funciones Un programa C consta de 1 o ms funciones. Un programa completo C tiene una a funcin denominada main(). o C no permite denir funciones dentro de funciones. La nalizacin de la funcin ocurre cuando se llega al nal de la funcin (}) o se o o o ejecuta una sentencia return(). Los parmetros se pasan siempre por valor. a Retornan por defecto un valor de tipo entero. Las funciones que no devuelven nada se denen de tipo void. Las funciones C pueden ser recursivas.

18 de febrero de 2004 Notas 11

4.10.

Variables

Las variables de C pueden ser: Externas: Son las que se denen fuera de cualquier funcin y, por tanto, son o potencialmente utilizables por muchas funciones. El campo de validez de una variable externa abarca desde el punto de su declaracin en un archivo fuente o hasta el n del archivo. Si se ha de hacer referencia a una variable externa antes de su denicin o si est denida en un archivo fuente que no es aquel en que se o a usa, es obligatoria una declaracin extern. o Automticas: Son las denidas dentro de las funciones. a Estticas: Las variables estticas (static) pueden ser internas o externas: a a Las variables estticas internas son locales a una funcin en la misma forma a o que las automticas pero, a diferencia de ellas, su existencia es permanente, a en lugar de aparecer y desaparecer al activar la funcin. Esto signica que o las variables estticas internas proporcionan un medio de almacenamiento a permanente y privado a una funcin. o Las variables estticas externas son accesibles en el resto del archivo fuente en a el que est declarada, pero no en otro. Por tanto, el almacenamiento esttico a a externo proporciona un medio de almacenamiento permanente y privado a un archivo (de hecho, no habr conictos con los mismos nombres en otros a archivos del mismo programa). Registro: Una declaracin register avisa al compilador que la variable ser muy o a usada. Cuando es posible se colocan en los registros de la mquina, lo que produce a programas ms cortos y rpidos. a a Existen algunas restricciones en las variables registro, que reejan la realidad del hardware subyacente: Pocas variables de cada funcin se pueden mantener en registros. o Slo se permiten algunos tipos. o La palabra register se ignora si hay declaraciones excesivas o no permitidas. No es posible tomar la direccin de una variable registro. o Las variables de C pueden denirse en una forma estructurada en bloques. Las declaraciones de variables (incluyendo inicializaciones) se colocan despues de la llave de apertura que introduce cualquier sentencia compuesta, no solamente al comienzo de una funcin. Las variables declaradas as se solapan con cualquier variable del mismo nomo bre en bloques externos, y permanecen hasta que se alcanza la llave de cierre.

18 de febrero de 2004 Notas 12

4.10.1.

Reglas de inicializacin de variables o

En ausencia de una inicializacion expl cita, se garantiza que las variables externas y estticas tendran inicialmente el valor cero. Las variables automticas y registro a a tienen valores indenidos (basura). Las variables externas y estticas slo pueden inicializarse mediante una expresin a o o constante; la inicializacin se realiza una sola vez, conceptualmente antes de que o comience la ejecucin del programa. o Las variables automticas y registro pueden inicializarse a valores no constantes a (cualquier expresin, incluyendo llamadas a funciones), y se inicializan cada vez o que se entra en la funcin o bloque. o

18 de febrero de 2004 Notas 13

4.11.

Entrada/salida

La entrada/salida bsica de caracteres se realiza mediante dos macros denidas en a <stdio.h>. Son: putchar(char c) int getchar() La entrada/salida de strings se realiza mediante las funciones: gets(char *linea) puts(const char *linea) La entrada/salida formateada se realiza mediante las funciones: printf(char *formato, arg1, arg2, ...) scanf(char *formato, arg1, arg2, ...) La conversin de strings a tipos bsicos y viceversa se realiza mediante las funciones: o a sprintf(char *s, const char *format, ...) sscanf(char *s, const char *format, ...)

18 de febrero de 2004 Notas 14

4.12.
4.12.1.

Estructuras de datos
Sinnimo (alias) o

C permite crear sinnimos de tipos de datos. Las principales razones para utilizar este o tipo de declaraciones son: 1. Facilitar la documentacin (y legibilidad) de un programa. o 2. Asociar nombres ms cortos a identicadores largos. a 3. Parametrizar un programa contra problemas de portabilidad. Si se utiliza typedef con los tipos de datos que pueden ser dependientes de la instalacin, slo se o o tendrn que cambiar los typedef cuando se lleve el programa a otro computador. a Una prctica com n es emplear typedef para las cantidades enteras y luego hacer a u las elecciones apropiadas de short, int y long en cada computadora. 4. Utilizar alg n programa que aproveche la informacion contenida en las declarau ciones typedef para realizar comprobaciones de tipos en el programa (por ejemplo, lint).

4.12.2.

Enumerado

A menos que se le asignen valores expl citos, el compilador asigna valores enteros constantes sucesivos comenzando por cero. La declaracin de un enumerado tiene las siguo ientes ventajas frente a las declaraciones equivalentes mediante macros: 1. El compilador puede detectar errores en las expresiones y asignaciones. 2. El depurador puede imprimir el valor simblico (en vez del valor entero). o

18 de febrero de 2004 Notas 15

4.12.3.

Array

Declaracin: Existen dos formas de declaracin de arrays: o o 1. Expl cita: cuando se especica el n mero de elementos del array. u 2. Impl cita: cuando el n mero de elementos del array lo calcula el compilador. u Los arrays multidimensionales se declaran mediante notacin de vectores (no mao tricial) y se almacenan por las. Acceso. El ndice de acceso del array es una expresin entera. Es responsabilidad o del programador garantizar que el valor del ndice est dentro de los l a mites del array.

18 de febrero de 2004 Notas 16

4.12.4.

Estructura (Registro)

Declaracin. Una estructura es un conjunto de una o ms variables, posiblemente o a de tipos diferentes, agrupadas bajo un mismo nombre para hacer ms eciente el a manejo. Las estructuras se denominan registros en otros lenguajes; por ejemplo, en Pascal. Opcionalmente puede seguir un nombre a la palabra clave struct; se lo denomina nombre de la estructura y se puede emplear en declaraciones posteriores como una abreviatura de la estructura. La llave de cierre que termina la lista de miembros puede ir seguida de una lista de variables. Si la declaracin de una estructura no va seguida de la lista de variables, no o se reserva memoria alguna; en este caso se est describiendo una plantilla de a la estructura. Se puede inicializar una estructura externa o esttica a adiendo a su denia n cin la lista de inicializadores de los componentes. o Las estructuras se pueden anidar. Podemos declarar arrays de estructuras. Acceso. Para referenciar un miembro de una estructura en una expresin, se o emplea la notacin punto. o nombre_de_la_estructura.miembro

4.12.5.

Restricciones de las estructuras

Las estructuras no pueden compararse; las unicas operaciones que se pueden realizar con una estructura son copiarlas o asignarlas como un todo, tomar su direccin (meo diante &), y acceder a uno de sus miembros.

18 de febrero de 2004 Notas 17

4.12.6.

Unin o

Una unin es una estructura donde todos los miembros tienen desplazamiento cero. La o estructura es lo sucientemente grande como para contener el mayor de los miembros, y la alineacin es la apropiada para todos los tipos de la unin. Es responsabilidad del o o programador recordar cual es el tipo que hay en la unin. Las uniones pueden aparecer o dentro de estructuras y arreglos, o viceversa. La notacin para acceder a un miembro o de una unin dentro de una estructura (o viceversa) es idntica a la empleada con o e estructuras anidadas.

4.12.7.

Campos de bit

La forma usual de manejar bits consiste en denir un conjunto de mscaras (en potena cias de 2) que corresponden a las posiciones de los bits. El acceso a los bits se convierte en un juego de operaciones de desplazamiento, enmascaramiento y complementacin. o C posee capacidad para denir y acceder directamente a campos denidos en el interior de una palabra, en lugar de hacerlo mediante mscaras. Un campo es un conjunto de a bits adyacentes dentro de un int. La sintaxis de la denicin de los campos se basa en o las estructuras. Los campos sin nombre (dos puntos y un tama o solamente) sirven de n relleno.

18 de febrero de 2004 Notas 18

4.13.

Punteros

Un puntero es una variable que contiene la direccin de otra variable. Los punteros se o utilizan con abundancia en C, debido a que: A veces son la unica manera de expresar un clculo. a Con ellos puede obtenerse un cdigo ms compacto y eciente. o a C proporciona dos operadores especiales para punteros: El operador unitario * toma su operando como una direccin y accede a ella para o obtener su contenido. El operador unitario & devuelve la direccin de un objeto. Slo puede aplicarse o o a variables y a elementos de un arreglo; construcciones como &(x + 1) y &3 son ilegales. Tambien es ilegal obtener la direccin de una variable de tipo register. o La declaracin del puntero p1 se entiende como un nemotcnico; se quiere indicar que o e la combinacin p1 equivale a una variable de tipo int. C permite declarar punteros o dobles (punteros a punteros). En general, un puntero se puede inicializar como cualquier otra variable, aunque normalmente los unicos valores signicativos son cero (NULL) o una expresin en que o aparezcan direcciones de objetos del tipo apropiado. Los punteros pueden aparecer en expresiones. Los operadores unitarios y & tienen mayor precedencia que los operadores aritmticos, por lo que al evaluar y = p1 + 1 e la expresin toma el valor del objeto al que apunta p1, se le suma 1, y el resultado se o asigna a y. Tambin pueden aparecer referencias a punteros en la parte izquierda de e una asignacin (p1 = 0; p1+ = 1). o La expresin (p1) + + necesita los parntesis; sin ellos incrementar el valor del o e a puntero p1 y no el objeto al que apunta, ya que los operadores unitarios y ++ se eval an de derecha a izquierda. u Los punteros se pueden comparar entre ellos o con cero (NULL). Como los punteros son variables, se pueden manipular igual que cualquier otra variable (p2 = p1). Como los parmetros de las funciones se pasan siempre por valor, cuando queremos a que la funcin devuelva valores mediante los parmetros tenemos que pasar punteros. o a

18 de febrero de 2004 Notas 19

4.13.1.

Punteros y arrays

En C existe una estrecha relacin entre punteros y arrays, ya que cualquier operacin o o que se pueda realizar mediante la indexacin de un array se puede realizar tambin con o e punteros. La versin con estos puede ser ms rpida pero, ms dif de entender. o a a a cil Si ptr apunta a un elemento particular de un array, entonces ptr i apunta al elemento que est i elementos antes de ptr, y ptr + i apunta al elemento que est i elementos a a despus. Si ptr apunta a a[0], entonces (p1 + i) se reere al contenido de a[i]. Esto es e cierto independientemente del tipo de variables del array. La denicin de sumar 1 a o un puntero, y, por extensin, toda la aritmtica de punteros establece que el incremento o e se adec a al tama o en memoria del objeto apuntado. En pa + i, i se multiplica por el u n tama o de los objetos a los que apunta pa antes de ser sumado a p1. n Cuando se pasa el nombre de un array a una funcin, se pasa la direccin del comienzo o o del array (un puntero). El paso de arrays multidimensionales a una funcin no requiere o especicar la primera dimension (es irrelevante porque lo que se trasmite realmente es, como antes, un puntero). En el ejemplo de la transparencia, la ultima declaracin de la o derecha indica que el parmetro es un puntero a un array de 4 enteros. Los parntesis a e son necesarios porque los corchetes [ ] tienen mayor precedencia que el operador ; sin parntesis. La declaracin int *tabla[4]; ser un array de 4 punteros a enteros. e o a

4.13.2.

Punteros y estructuras: notacin > o

Si p es un apuntador a una estructura, los miembros de la estructura se pueden referenciar mediante (*pd).miembro (hacen falta los parntesis porque la precedencia del e operador de miembro de estructuras es mayor que la de *). Sin embargo, como son tan frecuentes los apuntadores a estructuras se ha introducido en el lenguaje la notacin o abreviada >, que es equivalente.

4.13.3.

Punteros a funciones

En C es posible denir un puntero a una funcin, que puede ser manipulado, pasado o a funciones, colocado en arrays, etc. La declaracin de un puntero a funcin tiene la o o siguiente sintaxis: tipo (*nombre_puntero)() Es necesaria la primera pareja de parntesis. Sin ellos estar e amos haciendo una declaracin o de una funcin que devuelve un puntero a un entero. La llamada se realiza mediante: o (*nombre_puntero)(parametros)

18 de febrero de 2004 Notas 20

4.13.4.

Resumen de aritmtica de punteros e

En resumen, las operaciones aritmticas que podemos realizar con los punteros de C e son las siguientes: Pueden inicializar como cualquier otra variable, aunque normalmente los unicos valores signicativos son 0 (NULL) o una expresin en que aparezcan direcciones o de objetos del tipo apropiado. Puede sumarse o restarse un entero a un puntero. Pueden compararse. Pueden restarse punteros. No se permite sumar, restar, multiplicar, dividir, rotar, enmascarar punteros, ni sumarles valores oat o double.

18 de febrero de 2004 Notas 21

4.13.5.

Parmetros de la l a nea de comandos

Cuando se invoca main al comienzo de la ejecucin, se llama con dos parmetros. o a 1. El primero (llamado argc por convenio) es el n mero de argumentos en la l u nea de comandos con que se invoc al programa. o 2. El segundo (argv) es un puntero a un array de cadenas de caracteres que contienen los argumentos, uno por cadena. Por convenio, argv[0] es el nombre con el que se invoco el programa; por tanto, argc es como m nimo 1 (el primer argumento real es argv[1] y el ultimo es argv[argc-1]). Debido a que argv un puntero a un array de punteros, existen varias maneras de manipular los parmetros. a

18 de febrero de 2004 Notas 22

4.14.

Tratamiento de cheros

Para manejar cheros con formato en C necesitamos punteros a una estructura de datis denida en el chero <stdio.h> denominada FILE. Las principales funciones C para tratamiento de cheros son: 1. Apertura, cierre, renombrado y borrado: FILE *fopen(char *nombre, char *modo) El modo de apertura puede ser: r: modo lectura. w: modo escritura (crea el chero si no existe y descarta su contenido en caso de que ya existiese). a: modo a adir (si no existe lo crea). n Los modos r+, w+ y a+ tienen el mismo signicado que sus homlogos r, w, o a, pero se permite tanto la lectura como la escritura. Si el modo incluye una b (rb, r+b), signica que es un chero binario. En caso de error retorna un puntero NULL. int fclose(FILE *f) int rename(const char *antiguo nombre, const char *nuevo nombre) int remove(const char *nombre chero) 2. Transferencia: int fgetc(FILE *f) int fputc(int c, FILE *f) char *fgets(char *linea, int max linea, FILE *f) int fputs(char *linea, FILE *fp) int fscanf(FILE *f, char *format, ....) int fprintf(FILE *f, char *format, ....) 3. Posicionamiento: int fseek(FILE *f, long despl, int posicion) long ftell(FILE *f) void rewind(FILE *f); 4. Funciones de error: int ferror(FILE *f) int feof(FILE *f) Existen tres punteros de tipo FILE que son estndar y estn denidos en <stdio.h>: a a stdin, puntero a la entrada estndar, stdout, puntero a la salida estndar y stderr, a a puntero a la salida estndar de errores. Las funciones getchar() y putchar() son macros a denidas en <stdio.h> de la siguiente forma: #define getchar() getc(stdin) #define putchar(x) putc(x,stdout)

18 de febrero de 2004 Notas 23

4.15.

Errores frecuentes de los programadores de C

En este ultimo apartado se presentan los errores que comenten con ms frecuencia los a nuevos programadores de C.

18 de febrero de 2004 Notas 24

18 de febrero de 2004 Notas 25

18 de febrero de 2004 Notas 26

18 de febrero de 2004 Notas 27

4.16.

Programacin de estructuras de datos dinmicas con C o a

La combinacin de estructuras (registros) y punteros permiten crear estructuras cuya o forma y tama o no sea ja: estructuras de datos dinmicas. La unidad bsica de las n a a estructuras dinmicas es el nodo (registros que son enlazados mediante punteros para a formar la estructura deseada). En trminos generales las estructuras se pueden agrupar e en tres clases: estructuras lineales, estructuras jerrquicas, y grafos3 . a La declaracin de un nodo se realiza mediante una declaracin recursiva (una estructura o o que se autoreferencia ver transparencia). El miembro next es una variable de tipo puntero que contendr la direccin de otro nodo. a o Para utilizar las estructuras dinmicas es necesario solicitar memoria (mediante mala loc()) y liberar memoria (mediante free()). Para ello es necesario utilizar las rutinas denidas en el chero stdio.h. En las siguientes transparencias veremos cmo podemos implementar en C las siguo ientes estructuras dinmicas: a Pilas. Colas. Listas simplemente encadenadas. Listas doblemente encadenadas. Arboles.

Un grafo es una generalizacin de una estructura dinmica en la que cada nodo puede tener varios enlaces, o a y pueden existir bucles

18 de febrero de 2004 Notas 28

4.16.1.

Pila (LIFO)

Una pila es una estructura de datos que consta de dos operaciones: push() para insertar un elemento y pop() para extraer el ultimo elemento insertado. Por esta razn las pilas o tambin se denominan estructuras LIFO (del ingls Last In First Out). e e Las pilas pueden declararse mediante arrays. Esto tiene el inconveniente de que necesitamos reservar el espacio mximo necesario para ella. En la transparencia se presenta a la implementacin en C de una pila dinmica de datos de tipo entero. Utilizamos la o a variable global Top para apuntar a la cima de la pila. El algoritmo de las funciones push() y pop() es el siguiente: Push(): Creamos un nuevo nodo. Rellenamos la informacin del nuevo nodo. o Actualizamos el puntero de cima de pila al nuevo nodo. Pop(): Si la pila est vac retornamos un cdigo de error. a a o Guardamos en una variable auxiliar la informacin del nodo que est en la o a cima de la pila. Guardamos en un puntero auxiliar la direccin del nodo que est en la cima o a de la pila. Actualizamos el puntero de cima de pila al siguiente nodo. Liberamos el nodo que estaba en la cima de la pila. Retornamos la informacin que conten el nodo que hemos liberado. o a

18 de febrero de 2004 Notas 29

4.16.2.

Cola

Una cola es una estructura de datos que consta de dos operaciones: una para insertar un elemento al nal de la cola y otra para para extraer el elemento que est al principio a de la cola. Por esta razn las pilas tambin se denominan estructuras FIFO (del ingls o e e First In First Out). En esta implementacin utilizamos las variables globales principio y nal para apuntar o al primer y ultimo elemento de la cola. El algoritmo de las funciones insertar() y extraer() es el siguiente: Insertar() Creamos un nuevo nodo. Rellenamos la informacin del nuevo nodo. o Si la cola est vac actualizamos los punteros primero y siguiente al nuevo a a nodo. Si la cola no est vac insertamos el nuevo nodo al nal de la cola. a a Extraer() Si la cola est vac retornamos un error. a a Guardamos en variables auxiliares la informacin del primer elemento de la o cola (el nodo que vamos a liberar) y la direccin del segundo elemento de la o cola (que va a pasar a ser el nuevo primer elemento de la cola). Liberamos la memoria del primer elemento. Actualizamos el puntero al nuevo primer elemento de la cola. Si la cola est vac (el primer elemento es NULL), actualizamos el puntero a a al ultimo elemento de la cola a NULL. Retornamos la informacin del nodo que hemos extraido. o

18 de febrero de 2004 Notas 30

4.16.3.

Lista simplemente encadenada

La implementacin de una lista simplemente encadenada es similar a cola, pero se o diferencia en que los elementos se insertan y extraen de forma ordenada.

18 de febrero de 2004 Notas 31

Lista simplemente encadenada (cont.)

La funcin de busqueda recibe un parmetro de entrada (el dato a buscar n) y o a retorna dos parmetros de salida: a 1. El nombre de la funcin retorna un puntero al nodo donde se encontr el dato. o o 2. El parmetro de salida anterior es un puntero al nodo que hay antes (siguiendo a el orden de la lista) del nodo donde se encontr el dato. o De esta forma podemos utilizar la funcin buscar con dos nes: o 1. Saber la informacin solicitada est en la lista (ya que la funcin retorna NULL o a o cuando la clave no est en la lista). a 2. Facilitar la extraccin del nodo (ya que la funcin retorna las direcciones del nodo o o anterior al buscado y la direccin del nodo buscado, con lo que solamente necesitao mos actualizar los punteros observese la funcin Borrar en la transparencia). o Si anterior vale NULL signica que la clave se encontr en el primer nodo de la o lista.

18 de febrero de 2004 Notas 32

4.16.4.

Lista doblemente encadenada

En una lista doblemente encadenada cada nodo tiene dos punteros: un puntero al siguiente nodo (en orden) de la lista, y un puntero al nodo anterior. Como puede apreciarse en la transparencia, la funcin de insercin es similar a la funcin de insercin o o o o en una lista simplemente encadenada (slo hay que tener cuidado en la actualizacin o o del nuevo puntero).

18 de febrero de 2004 Notas 33

4.16.5.

Arbol binario

El arbol binario es probablemente la estructura dinmica ms utilizada. Cada nodo a a de la estructura tiene un predecesor (padre o ancestro) y uno o dos sucesores (hijo o descendientes). La transparencia contiene el pseudocdigo de un algoritmo de recorrido in-order itero ativo del arbol binario. El algoritmo consiste en comenzar profundizando en el arbol slamente por la rama izquierda de los nodos a la vez que almacenamos en la pila la o direccin de estos nodos (porque a n no los hemos procesado). o u Al llegar a lo ms profundo del arbol por su rama ms izquierda, en la cima de la a a pila tenemos el primer nodo que debemos procesar. Por lo tanto, extraemos el nodo de la pila, lo procesamos, e intentamos profundizar a partir de su hijo derecho. Si no tiene hijo derecho, sacamos otro nodo de la pila (el padre), lo procesamos e intentamos profundizar por su hijo derecho. Este proceso se repite hasta procesar el arbol completo. El algoritmo recursivo es quizs ms fcil de entender. Es bsicamente el mismo ala a a a gorimo, lo que en este caso la pila se implementa de forma automtica mediante las a llamadas recursivas.

18 de febrero de 2004 Notas 34

Arbol binario (cont.) La insercin en el arbol binario sigue el siguiente algoritmo: o Si el arbol est vac la raiz es el nuevo nodo que ibamos a insertar. a o, Si el arbol no est vac buscamos la posicin en el arbol donde debemos insertar a o, o el nodo y actualizar el puntero correspondiente.

También podría gustarte