Вы находитесь на странице: 1из 27

9.

APUNTADORES EN C

9.1. MOTIVACIN Cuando nos enfrentamos a un problema que tenemos planeado resolver con un programa escrito en C, empezamos modelando dicho problema (i.e. haciendo explicitos cuales son los datos de entrada y salida del problema y como los representaremos en el computador). Este modelaje, hasta el momento a supuesto que el tipo de informacin y la cantidad de est, es conocida. Este no es el caso en muchos programas, por ejemplo en un programa para calcular la mediana de las calificaciones de un curso, se requieren todas las calificaciones; la cantidad de calificaciones dependen de la cantidad de estudiantes y este puede ser un dato desconocido al desarrollar el programa. Muy probablemente la estructura de datos escogida para almacenar las calificaciones sera un vector de flotantes. Cuantos elementos debe tener el vector? 30? 50?. No faltar el prctico que declare un vector de 1000 posiciones, pero si tal programa se emplea para un curso de 15 estudiantes, habr 985 posiciones del vector desperdiciadas. Una de las aplicaciones de los apuntadores es permitir manejar estructuras que pueden cambiar de tamao en tiempo de ejecucin (es decir mientras el usuario final esta empleando el programa), en el ejemplo anterior, puede emplearse un apuntador y memoria dinmica para crear un vector con tantas posiciones como lo desee el usuario final. Los apuntadores tambin facilitarn la creacin y mantenimiento de estructuras mucho ms complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicacin de apuntadores ser permitir variables por referencia en las funciones. Es decir permiten que una funcin modifique el contenido de las variables que recibe como parmetros. Adems dada la intima relacin entre vectores y apuntadores, las operaciones con vectores, como se ver, pueden tambin hacerse con apuntadores y en muchos casos con ms facilidad. La total comprensin del uso de apuntadores requiere un conocimiento ms profundo del hardware y del sistema operativo, la seccin 9.2. est dedicada a tratar algunos conceptos claves de estos temas. Despes en la seccin 9.3. se tratar la sintaxis para declarar y usar apuntadores y se explicar su semntica. Finalmente en la seccin 9.4. se pondr en prctica la teora, y se darn varias aplicaciones, entre ellas el uso de memoria dinamica.

9.2 LAS VARIABLES EN LA MQUINA Para comprender que es y como se usa un apuntador es til comprender como se representan las variables en la memoria del computador. Primero que todo recordemos que la memoria del computador puede representarse como un conjunto de celdas (o mejor un vector de celdas), cada una de las cuales puede almacenar un byte1. Para el caso mostrado en la Figura 1 se tiene una memoria que podr almacenar m bytes m Figura 1 Supongamos que hacemos el siguiente programa: 0 1 . . .

int main() { int a; a=0; return a; }

Cuando se ejecute este programa, se crear una zona de memoria dedicada a la variable a, en la figura 2.1. se muestra esta zona, se trata de las posiciones i e i+12. Cuando se escriban datos en la variable a, se escribir en la zona 0 0 reservada para a, y cuando se extraiga el 1 1 valor de a, se sacar el dato almacenado . en esa zona. . i 75 Zona i 0 Ya es ms claro porque las variables i+1 23 de a i+1 0 deben ser inicializadas siempre? . Que haba en la zona de la variable a . antes de que esta fuese reservada? No m m podemos saberlo, podra ser 0, 8, 1043, Figura 2.1. Figura 2.2. cualquier nmero dejado all por otros programas, o por el sistema operativo. El programa de ejemplo, despus llenar el espacio reservado para a, con el valor 0, como se muestra en la figura 2.2. Y finalmente recuperar el contenido de la variable a (es decir la informacin que hay en la zona de a) y lo retornar. Como el valor contenido en la zona de a es 0, retornar 0.

1 2

Un byte es una agrupacin de 8 bits. Un bit puede pensarse como un 0 o un 1. Vale la pena aclarar que las direcciones de memoria no son necesariamente nmeros enteros. La expresini+1, se refiere a la posicin de memoria que le sigue a la posicin i.

Nota Histrica Ahora surgen nuevas preguntas: Cuando comenz el auge de los computadores personales, a principios de la dcada de los 80, eran comunes computadores cuyas memorias no sobrepasaban los 64 KB (1KB equivale a 1024 bytes), tal era el caso del ZX Spectrum, Commodore 64, Color Computer I y II, etc. El procesador 8088 de Intel permita direccionar hasta 1 MB de memoria (1MB equivale a 1024KB). Esto fue aprovechado por IBM que lanz sus primeros PC con 512KB en RAM. Tiempo despus aparecieron nuevos procesadores para microcomputadores, que permitan direccionar ms memoria.

Como se escoge el sitio donde quedar cada variable en memoria? De que tamao es la zona de memoria reservada? Si el computador slo trabaja con unos y ceros como se puede trabajar con variables de tipo entero, flotante, carcter y vector? Como se asegura que la zona de memoria reservada para una variable no ser escrita por otra variable del programa o por el programa mismo?

9.2.1. Representacin de datos Ya sabemos que las variables son en realidad zonas de memoria, asignar un valor a una variable equivale a llenar con el valor dado la zona de la variable, y recuperar el valor de una variable equivale a sacarlo de la zona de memoria. Ahora se tratar de responder la pregunta: Como almacena el computador un entero, un flotante, un carcter? o en otras palabras Como representa los datos el computador? Los computadores actuales trabajan con 2 niveles de voltaje (e.g. +5V y 0V), uno es el nivel alto y otro el nivel bajo, al nivel alto de voltaje se le asocia con el 1 y al nivel bajo con el 0. Las memorias de los computadores se basan en circuitos que pueden almacenar uno de estos niveles de voltaje3 (es decir pueden almacenar un bit). As mismo toda la transmisin de informacin por los buses y cables, se presenta como seales de voltaje (nivel alto y nivel bajo). En resumen toda la informacin que puede trabajar un computador debe en ltimas representarse como secuencias de unos (1) y ceros (0). Como slo tenemos disponibles los dgitos 0 y 1, para representar la informacin empleamos nmeros en base 2. Por ejemplo el entero 6 (escrito en base 10), se escribe como 110 en base 2. Esta secuencia de dgitos (110) si puede ser almacenada en la memoria del computador empleando 3 flip-flops o su equivalente: El primero almacena un nivel alto de voltaje, el segundo un nivel alto, y el tercero un nivel bajo. Para representar caracteres se emplea la tabla ASCII (Ver el Apendice B), de forma que todo carcter se representa como un nmero entero y este a su vez se expresa en base 2. Por ejemplo el valor ASCII asociado a la letra C es 67, entonces en el computador se almacenar como la secuencia de bits: 1000011.
3

Un Flip-Flop es una componente electrnico que permite almacenar un bit, ya sea 1 (nivel alto de voltaje) o 0 (nivel bajo)

Ya se explico como se representan los enteros positivos. Para representar enteros tanto positivos como negativos se emplea uno de los bits del nmero que es 0 si el signo es positivo y 1 si es un entero negativo (a este bit se le llama bit de signo). Adems se trabaja en complemento a 2 (puede consultar la bibliografa para saber en que consiste). La representacin de nmeros flotantes es un poco ms complicada, la mayora de computadores se han acogido a un estndar definido por la IEEE. Cada nmero flotante se escribe empleando notacin cientfica, como un dgito, con una expansin decimal truncada (es decir slo se toma cierta cantidad de dgitos) y un exponente. (e.g. 54.32 se escribe como 5.432E-1). El lector interesado puede remitirse a la bibliografa.

9.2.2. Tamao de los tipos de datos Las secuencias de unos y ceros que puede almacenar un computador, se agrupan en bytes (ocho (8) bits forman un (1) byte). Como sabemos cuando se declara una variable de tipo entero, se reserva un espacio en la memoria del computador, para mantener el valor de tal variable. El tamao de la zona reservada, depende del tipo de variable que hayamos declarado y del computador en el cual este ejecutndose el programa. Este tamao reservado limita los valores que puede tomar la variable. Por ejemplo si se reservan 2 bytes para almacenar nmeros enteros, podr mantener secuencias de a lo sumo 16 unos y ceros, es decir podr representar a lo sumo 65536 valores diferentes (el primero corresponde a la secuencia 0000000000000000 y el ltimo corresponde a la secuencia 1111111111111111). Como se dijo este tamao depende de la mquina en la que se este ejecutando el programa, del compilador y del tipo, algunos tamaos tpicos se listan en la tabla 14

Maquina PC/BC++ DEC PDP-11 Honeywell 6000 IBM 370

int

float

char

long int

double

16 bits 16 bits 36 bits 32 bits

32 bits 32 bits 36 bits 32 bits

8 bits 8 bits 9 bits 8 bits Tabla 1

32 bits 32 bits 36 bits 32 bits

64 bits 64 bits 72 bits 64 bits

9.2.3. Duracin de variables en C En los programas realizados hasta ahora se ha visto que las variables declaradas slo existen al interior del bloque donde fueron declaradas y slo pueden usarse all, excepto las variables globales cuya existencia se asegura a lo largo de todo el programa y pueden ser usada desde cualquier parte. Esto se debe al sitio donde C reserva la zonas de memoria para las variables. Para las variables globales se reserva una zona de memoria destinada con ese propsito, que ninguna otra parte del programa emplea, y que puede ser accedida por cualquier funcin del programa. Por el contrario para las variables locales, se reserva una zona temporal que slo existe mientras que la funcin donde se declar la variable est ejecutndose.
4

Tomado de [2]. Pag.36

Este comportamiento puede ser modificado empleando los prefijos static, auto y register al declarar variables. Para conocer como afectan la duracin de las variables, puede referirse a la bibliografa. PREGUNTAS 1. Escriba en base 2 los siguientes enteros (que estn escritos en base 10): a) 7 b) 8 c) 45 d) 192 e) 2334 2. Cuantos bytes se requieren para almacenar un flotante en un PC? 3. Cuantos valores distintos podr almacenar una variable tipo carcter en una mquina IBM PC? 4. Cuantos enteros distintos podr representarse con una variable tipo long int en una mquina Honeywell 6000? 5. a) Explique como se declaran variables globales y como pueden usarse. b) Que diferencias hay con respecto a variables locales? 6. Que capacidad de memoria RAM tienen los supercomputadores?

9.3. QUE ES UN APUNTADOR? La discusin desarrollada en la seccin 9.2 pretende dar algunas nociones bsicas que permitirn entender con mayor facilidad el concepto de apuntador.
int main () { int a; int *ap; a=0; ap=&a; *ap=1; return a; }

El ejemplo mostrado es un programa muy sencillo que ilustra el uso de apuntadores. Primero se declara una variable a de tipo entero y una variable ap tipo apuntador a entero. En memoria se reserva un espacio para la variable a (supongamos que estamos trabajando en una mquina 80x86 bajo DOS con BC++, entonces sern 2 bytes) y otro para el apuntador (en maquinas 80x86 bajo DOS con BC++ sern 4 bytes), este proceso se ilustra en la figura 3.1. La variable a tiene asociados los bytes i e i+1, y la variable ap tiene asignados los bytes j, j+1,j+2 y j+3.

Cuando se ejecute la asignacin a=0, las posiciones i e i+1 obtendrn el valor 0 (sus 16 bits estarn en 0). El operador & es un operador unario prefijo, que se aplica sobre variables y retorna la direccin de la variable que opera (esto es la posicin en memoria donde comienza la zona reservada para la variable), por ejemplo &a ser la direccin de la variable a, en el caso del ejemplo ser i. Las direcciones, no se consideran como enteros ni como flotantes ni como caracteres en C. Por esta razn no puede asignarse una direccin a una variable de alguno de estos tipos. En el programa de ejemplo se hace la asignacin ap=&a, es decir asignamos la direccin de a a la variable ap. Esta asignacin si es valida porque ap est declarado como apuntador a entero. Cuando se realice la asignacin, el espacio en memoria de ap contendr la direccin i. 0 1 . . i . .
a

ap

m Figura 3.1.

La utilidad de los apuntadores es que permiten referirse directamente a direcciones de memoria. En nuestro ejemplo ap podr referirse a la direccin i (que corresponde a la zona de memoria de la variable a). Para referirse a la direccin de memoria contenida en un apuntador se usa el operador unario prefijo *. As en la asignacin *ap=1, estamos asignando el entero 1 a la direccin apuntada por ap, es decir asignamos 1 a la zona de memoria iniciada en la direccin i. Como efecto de esta operacin el valor de la variable a cambia de 0 a 1 (precisamente porque modificamos la zona reservada a la variable a). Cuando se ejecute la instruccin return a, el programa terminar y retornar el valor 1.

9.3.1. Declaracin de apuntadores La declaracin de apuntadores puede realizarse en cualquier parte del programa donde sea valida la declaracin de variables. La sintaxis es:
<tipo> *<identificador>;

Donde <tipo> es un tipo valido en C (e.g. int, long <identificador> es un nombre de variable vlido.

int, double, etc.), e

<tipo> ser el tipo de datos a los cuales apuntar la variable de nombre <identificador>.

Algunos ejemplos de declaraciones correctas son:


int *ap_a_entero; char *dcar; unsigned long int *ap_a_numerote; void *apuntador_generico;

La variable dcar ser de tipo apuntador a carcter, esto significa que contendr la direccin donde comienza un carcter. La variable ap_a_numerote podr contener la direccin donde comienza un entero largo sin signo.

9.3.2. Asignacin de apuntadores A una variable de tipo apuntador slo pueden asignrsele una direccin de memoria. Para obtener la direccin de una variable (inclusive de un apuntador), se emplea el operador & a la izquierda del nombre de la variable.5 El operador & slo puede aplicarse a variables y a elementos de un arreglo, no es valido &3 o & (x+1). Tampoco puede aplicarse este operador a variables declaradas como register. La direccin de una variable no puede ser asignada, el siguiente ejemplo muestra que NO es valido con el operador &:
int a,b; &a=&b;

A una variable tipo apuntador puede asignrsele el valor de otra variable del mismo tipo como se ejemplifica a continuacin:

double d; double *pd1; double *pd2; pd1=&d;


5

Tambin puede asignarse otras direcciones de memoria, pero debe conocerse el modo de direccionamiento de la mquina y tener grandes precauciones en el uso de tales apuntadores.

pd2=pd1;

El operador & tiene mayor precedencia que los operadores aritmticos. A una variable de tipo apuntador, slo pueden asignrsele direcciones de memoria, la nica excepcin es el entero 0. El valor 0 es tratado de forma especial con apuntadores, cuando se le asigna 0 a un apuntador estamos diciendo que tal variable no est apuntando a zona alguna de memoria. En la librera stddef.h se declara la constante NULL con el valor 0, este es el smbolo que emplearemos para referirnos a apuntadores que no estn direccionando alguna zona de memoria. A continuacin se muestra un ejemplo de su uso:
int *api;/* !!Peligro. apint apunta a algun sitio desconocido */ api=NULL; /* Ahora api no apunta a zona alguna */

9.3.3. Uso de apuntadores Para referirse a la posicin de memoria contenida en un apuntador se usa el operador * a la izquierda del apuntador. A diferencia del operador &, este operador si puede colocarse al lado izquierdo de una asignacin (as como al lado derecho). Por ejemplo:
char c; char *pc; pc=&c; *pc=c; c=*pc+2;

El operador * slo puede aplicarse a variables de tipo apuntador. El siguiente ejemplo muestra un empleo incorrecto de este operador
int c; *c=0;

Este operador tiene mayor precedencia que los operadores aritmticos.

9.3.4. Operaciones entre apuntadores Ya se explico que no pueden asignarse enteros a variables de tipo apuntador, ni direcciones a variables de tipo entero. An as hay varias operaciones en las que pueden emplearse enteros y apuntadores, estas son adicin (+) y resta (-). Veamos un ejemplo

float f[5]; float *apf1; float *apf2; int d; apf1=&(f[0]); apf2=apf1; apf1=apf1+3; apf2=apf1-2; d=apf1-apf2;

En este ejemplo se declaran dos apuntadores a flotantes, un vector para 5 flotantes y un entero. Primero se asigna la direccin del comienzo del vector f al apuntador apf1 .(la asignacin tambin pudo haberse hecho con apf1=f;) Despus esa misma direccin es asignada a la variable apf2.

Analicemos ahora la asignacin apf1=apf1+3. La expresin apf1+3 ser la direccin de apf1 incrementada en tres posiciones (teniendo en cuenta que apf1 apunta a flotantes), es decir equivale a la direccin del cuarto flotante del vector f (porque apf1 apunta al primero). La nueva direccin as calculada se asigna a apf1. En la instruccin apf2=apf1-2, asignamos a la variable apf2 la direccin del segundo flotante del vector f. Finalmente en la instruccin d=apf1-apf2 obtenemos la distancia (en flotantes) entre los apuntadores apf1 y apf2, esto es la cantidad de flotantes que pueden ponerse entre ambas direcciones, para este caso particular d obtendr el valor 2. Por ahora no es tan importante que comprenda el ejemplo en su totalidad pero ser conveniente que vuelva a este ejemplo despus de ver la utilidad de los operadores + y - al trabajar con vectores.

9.3.5. Representacin grfica de apuntadores Para referirnos a vectores, es usualmente ms cmodo emplear una grfica como la siguiente: v1: 0 1 2 ... n-1

Cuando se trata de apuntadores tambin suele emplearse una representacin grfica, se trata de una celda de la cual parte una flecha. El otro extremo de la flecha se ubica en otra celda, la cual representa la variable (o zona de memoria) que est siendo apuntada. Para ejemplificar como se usa esta representacin grfica retomaremos el ejemplo 1 de la seccin 9.3. Para cada operacin importante del programa (con respecto a las variables a y ap) se hacen los diagramas correspondientes.

int main () { int a; int *ap; a=0; ap=&a; *ap=1; return a; }

1. ap 3. ap a 0 a

2. ap 4. ap

0 a 1 a

Empleando estos esquemas, se pretende ocultar la forma real de operacin de los apuntadores (es decir a nivel de memoria).

PREGUNTAS 1. Supongamos que a y b son variables de tipo entero, cual ser el valor final de b despus de ejecutar las siguientes instrucciones?
a=a*a; b=*(&a);

2. La siguiente declaracin es valida:


int **app;

De que tipo es la variable app? Como puede asignarse un valor a esta variable? Cmo puede referenciarse memoria con ella? 3. Qu funcin conoce que reciba un apuntador como uno de sus parmetros? (Ayuda: repase las funciones de la librera stdio.h) Cmo cree que es el encabezado (prototipo) de esa funcin? 4. En el ejemplo de la seccin 9.3.3. Cual es el valor final de *pc y el de c? 5. Es el siguiente programa correcto? Cual es el valor final de a?
int a; int *b; a=2; b=&a; *b=(*b)*(*b);

6. Haga un diagrama de la memoria y explique paso a paso lo que ocurre al ejecutar el programa de ejemplo de la seccin 9.3.4. 7. Porque cree que se considera peligroso el uso de apuntadores? Que puede ocurrir con un apuntador no inicializado? 8. Como se representa grficamente el apuntador a apuntador del ejercicio 2?

9.4. APLICACIONES DE APUNTADORES

9.4.1. Variables por referencia En PASCAL pueden pasarse argumentos a funciones o procedimientos por valor o por referencia (Empleando la palabra reservada VAR en los parmetros del encabezado de la funcin, se pasan variables por referencia). En C por el contrario siempre se pasan parmetros por valor, es decir que una funcin no puede modificar globalmente el valor de sus parmetros. Los cambios que haga a sus parmetros, slo sern locales a la funcin, una vez, esta termine y la funcin llamadora retome el control, el valor de los argumentos pasados ser el mismo. Sin embargo muchas veces requerimos que una funcin modifique el valor de sus parmetros en el resto del programa. Los apuntadores dan un medio para lograr esto: podemos hacer funciones que reciban como parmetros apuntadores a los valores y no los valores mismos. Una funcin que requiere modificar sus parmetros, es la funcin que intercambia el valor de dos variables. No podemos hacer una funcin que reciba dos enteros e intercambie sus valores, pero si una que reciba dos apuntadores a enteros e intercambie los valores referenciados por los apuntadores.
#include <stdio.h> void intercambia(int *x, int *y); /* Intercambia los valores de x e y */ int main () { int a,b; a=100; b=200; intercambia(&a,&b); printf(a=%i, b=%i,a,b); } void intercambia(int *x, int *y) { int t=*x; *x=*y; *y=t; }

Vale la pena aclarar que los apuntadores son tratados como tipos en C, por esta razn es posible declarar funciones que retornen apuntadores.

9.4.2. Vectores y apuntadores Los vectores en memoria son zonas de memoria consecutiva. As un vector de 20 flotantes, en memoria corresponde a una zona de memoria donde hay espacio para 20 flotantes (en el caso del IBM PC, esto equivale a 20*4 = 80 bytes). Un vector, como buena zona de memoria, tiene una direccin donde comienza, es posible tener un apuntador con tal direccin. An ms es posible referenciar por medio de apuntadores, cualquier posicin del vector.

Estudiemos en detalle el ejemplo presentado: Primero reservamos espacio para un contador (cont), una zona capaz de almacenar 20 flotantes de doble precisin consecutivos (vec), y una para almacenar una direccin (pvec). Despus a pvec le asignamos la direccin de la primera

int cont; double vec[20]; double *pvec; pvec=&vec[0]; cont=0; while (cont<20) { *pvec=(double)cont; cont++; pvec=pvec+1; }

posicin del vector vec (esto es la posicin cuyo ndice es 0). El estado de las variables puede representarse grficamente as:

pvec vec :

cont 0

? ? ? ... ? ? 0 1 2 19

La variable cont es inicializada en 0, y entramos en un ciclo que se repetir 20 veces. En cada iteracin le asignamos al nmero apuntado por pvec el valor de cont. Note que para esta asignacin hacemos un casting o conversin de tipo. Convertimos el valor de tipo int almacenado en cont, al mismo valor pero con tipo double. Empecemos con la primera iteracin: cont tiene el valor 0, entonces en la posicin 0 de vec almacenamos un 0, despus incrementamos el valor de cont (queda en 1), y as mismo incrementamos en uno el valor de pvec. Este incremento, como se explic en la seccin 9.3.4, hace que la nueva direccin que pvec contiene sea la de un dato delante del que apuntaba, es decir la del double siguiente. En este caso el siguiente dato corresponde a la posicin 1 del vector vec. Despus de la primera iteracin el estado de las variables ser:
pvec vec : cont 1

0 ? ? ... ? ? 0 1 2 19 Otra forma de hacer tal incremento es empleando el operador ++ prefijo o postfijo: pvec++ o ++pvec. Despus de la segunda iteracin:
pvec vec : cont 2

0 1 ? ... ? ? 0 1 2 19

y finalmente despus de la vigsima:


pvec vec : cont 20

0 1 2 . . . 18 19 0 1 2 18 19

Se recomienda volver ahora a la seccin 9.3.4. y repasar el ejemplo y los operadores +, y - con apuntadores. Note que despus del ciclo el apuntador pvec, queda apuntando a una zona de memoria que no hace parte del vector vec. En general las operaciones sobre vectores con apuntadores suelen abreviar an ms los programas y eventualmente volverlos ms eficientes, pero esto se paga con dificultad para leer y entender los programas.

Para completar, C internamente trata los vectores como apuntadores. vector


int vi[10];

Cuando declaramos un

C entiende que vi ser un apuntador a enteros, lo pone a apuntar a una zona de memoria con espacio para 10 enteros y no permite que el programador cambie la direccin a la que vi apunta. La primera ventaja radica en poder tratar la variable vi como un apuntador (excepto que no podemos cambiar su valor), podemos asignar directamente el valor de vi a otro apuntador a entero:
int *pvi=vi;

Cuando se referencia alguna posicin del vector vi, por ejemplo la posicin 5:
vi[5]=0;

C traduce y entiende:
*(vi+5)=0;

que significa el sexto entero a partir del apuntado por vi (lo mismo que vi[5]). En los encabezados de funciones, hemos recibido vectores sin especificar su tamao, como en la siguiente caso:
int strlen(char s[]);

Esta declaracin es equivalente a:


int strlen(char *s);

slo que en el primer caso, al interior de la funcin no podr cambiarse el valor de la variable s, mientras que en el segundo caso si. Ahora ilustraremos el uso de apuntadores para manejo de cadenas:
int strlen(char *s) { /* PRE: Recibe una cadena */ char *p; for (p=s;*p!=\0;p++); return p-s; /* POST: Retorna la longitud de la cadena recibida */ }

En el ejemplo anterior, podemos reemplazar la condicin del for (i.e *p!=\0), simplemente por *p. Funciona porque \0 es el caracter cuyo valor ASCII es 0. Entonces si vale *p==\0 es porque *p tiene el valor ASCII 0, y por tanto *p!=\0 es falso. (Recuerde que en C, el entero 0 equivale a falso y cualquier otra valor entero es verdadero)

char *strchr(char *s,char c) { /* PRE: Recibe una cadena s y un caracter c */ for (;*s!=\0 && *s!=c;s++); if (*s==\0) { return NULL; } return s; /* POST: Retorna un apuntador al primer sitio dentro de la cadena donde este el caracter c. En caso de no estar retorna NULL */ }

De la funcin anterior es importante destacar que retorna un apuntador a caracter, que emplea la constante NULL para indicar que no encontr el caracter c dentro de la cadena s. Otra caracterstica importante es que no requiere variables globales, emplea al parmetro s, para recorrer la cadena. Esta funcin ilustra plenamente la simplificacin que pueden tener las funciones cuando se emplean apuntadores, pero tambin muestra lo complicada que puede ser su lectura. En general los parmetros de las funciones de las libreras string.h, son apuntadores. Volvamos a la discusin iniciada al final de la seccin anterior: Aritmtica de apuntadores, para ello ilustraremos con ejemplos las operaciones permitidas:
int vent[20]; int *ape1,*ape2; ape1=vent; ape2=ape1+5; /* Suma de apuntador con entero */ ape1=ape2-5; /* Resta de apuntador con entero */ vent[0]=ape2-ap1; /* Resta de un apuntador con otro apuntador */ ape1++; /* Incremento */ *ape1=ape1<ape2; /* Tambin es valida la comparacin de apuntadores */ /* cando ambos apuntan a elementos de un mismo vector */ /* Dara verdadero y lo asignar a vent[0] */ *ape2=ape1>ape2;

SISTEMA OPERATIVO 9.4.3. Memoria dinmica La memoria es un importante recurso del computador, es necesario administrarla de alguna forma para asegurar que no se desperdicie o malgaste. Una de las funciones de un sistema operativo es administrar la memoria. Un sistema operativo (SO) puede ser contemplado como una coleccin organizada de extensiones software del hardware, consistente en rutinas de control que hacen funcionar un computador y proporcionan un entorno para la ejecucin de los programas. Otros programas se apoyan en las facilidades proporcionadas por el sistema operativo para obtener acceso a los recursos del sistema informtico, tales como archivos y dispositivos de entrada/salida (E/S). Los programas invocan generalmente los servicios del sistema operativo por medio de llamadas al sistema operativo. Adems los usuarios pueden interactuar con el sistema operativo directamente por medio de rdenes del sistema operativo. En cualquier caso, el sistema operativo acta como interfaz entre los usuarios y el hardware de un sistema informtico. Internamente, un sistema operativo acta como gestor de los recursos del sistema informtico, tales como el procesador, la memoria, los archivos y los dispositivos de E/S. [4]

Cuando se ejecuta un programa, el sistema operativo le asigna varias zonas de memoria para que

las emplee, se tratan de una zona para los datos del programa, una zona para el programa mismo y otra para el control de la ejecucin y datos temporales. Cada programa debe usar slo la memoria que le ha sido asignada, de no ser as un programa inquieto podra leer informacin de otras zonas de memoria o an peor escribir en partes que pertenecen al sistema operativo o a otros programas. El sistema operativo DOS es especialmente frgil en la administracin de memoria, a diferencia de otros sistemas como UNIX o Windows NT, donde el sistema vela para que ningn programa (excepto los designados en el kernel) escriba o lea de zonas de memoria que no le corresponden. Una vez se termina la ejecucin de un programa, el sistema operativo debe encargarse de liberar la memoria que haba asignado para ese programa, y de esta forma hacerla disponible para otros programas.

Puede ocurrir que un programa requiera ms memoria de la que el sistema operativo le asign al iniciar la ejecucin, esto puede hacerse desde C empleando varias funciones para administracin de memoria. Algunas de estas funciones estn declaradas en la librera stdio.h y otras en la librera alloc.h:
void void void void *malloc(int size); free(void *block); *calloc(int nitems, int size); *realloc(void *block, size_t size);

Por ahora nos dedicaremos a las funciones malloc y free. Con la funcin malloc, un programa puede pedir una cantidad determinada de memoria, el compromiso que adquiere entonces es liberar esa memoria antes de terminar la ejecucin con la funcin free. ADVERTENCIA Es muy importante liberar la memoria que se solicite, porque de lo contrario habr memoria reservada sin emplear, memoria que otros programas o el sistema operativo podran necesitar. As mismo no debe emplearse memoria que no haya sido localizada o que haya sido liberada. 9.4.3.1. Apuntadores genricos: Las funciones malloc y free trabajan con apuntadores a void, estos son apuntadores genricos, que pueden ser convertidos a otros tipos de apuntadores por medio de casting. Veamos un ejemplo de como hacer casting entre apuntadores y de sus peligros:
int *apin; float *apfloat; int e; apin=&e; apfloat=(float *)apin; *apfloat=10.5;

En el contexto de este programa una asignacin como:


apfloat=apin;

no es valida, porque apfloat es un apuntador a flotante mientras que apin es un apuntador a entero, cuando se compilar reportara un error. Sin embargo la asignacin
apfloat=(apfloat *)apin

si es valida, har que apfloat y apint apunten a la misma direccin de memoria An as este programa tiene un gravsimo problema cuando se ejecute. Haciendo un diagrama de memoria (suponiendo que estamos ejecutndolo en un sistema donde los tipos float e int tengan diferente tamao) puede notarse el problema que genera la ltima instruccin.

9.4.3.2. malloc: Esta funcin recibe como parmetro el tamao, en bytes, del bloque de memoria que el programa requiere. Si hay suficiente memoria para reservarlo, retorna un apuntador al bloque recin localizado. Si la memoria no alcanza retorna NULL. Supongamos que estamos trabajando en un IBM PC, y queremos pedir espacio para almacenar un double. La siguiente porcin de programa realiza correctamente esta tarea (con excepcin de que no libera memoria y tampoco verifica si malloc logra localizar memoria):
double *apd; apd=(double *)malloc(8); *apd=0.00023222;

Como resulta muy incomodo tener que conocer el tamao en bytes de cada tipo de datos que queremos localizar C ofrece la primitiva sizeof, que recibe como parmetro un tipo o expresin y retorna el tamao en bytes del tipo correspondiente. El ejemplo anterior empleando sizeof puede quedar as:
double *apd; apd=(double *)malloc(sizeof(double)); *apd=0.00023222;

Otra gran ventaja de sizeof es que no compromete la portabilidad del programa. Un programa escrito en C es portable, cuando al pasar las fuentes de una mquina a otra, por compiladores apropiados para cada mquina, se obtiene programas ejecutables que funcionan igual en ambas mquinas. Todos los programas que hemos escrito hasta el momento son portables (excepto el primer ejemplo de esta seccin y la porcin anterior que dice apd=(double *)malloc(8);), es decir que si los compilamos en una mquina IBM PC (por ejemplo con el compilador BC++ 3.1) o si los compilamos en un 486 (por ejemplo con el compilador gcc), obtendremos programas ejecutables en cada uno de ellos que se comportan exactamente igual. 9.4.3.3. free: Recibe un apuntador a un bloque de memoria previamente localizado con alguna de las funciones para localizar memoria: malloc, calloc y realloc. Libera tal bloque para que pueda ser empleado por otros programas o por otras partes del mismo programa. Si reserva un bloque de memoria y luego lo libera con free, no debe volver a referenciar tal bloque de memoria (ya no est asignado y contendr informacin de otra parte del programa) Completemos el ltimo ejemplo (an falta verificar que hubiese memoria):
double *apd; apd=(double *)malloc(sizeof(double)); *apd=0.00023222; free(apd);

El programa mostrado a continuacin es un ejemplo completo del uso de #include <string.h> memoria dinmica. #include <stdio.h> #include <alloc.h> Note que este programa verifica que malloc asigne la memoria solicitada, int main() en caso de que malloc falle en asignar { los 10 bytes solicitados, terminar y char *cad; retornar 1. /* Localizamos memoria */ Si malloc logra localizar la memoria, cad = (char *) malloc(10); copia en la zona asignada la cadena if (cad==NULL) { "Hola" terminada en '\0', es decir printf("No hay Memoria\n"); emplea 5 de los 10 bytes localizados y return 1; apuntados por cad. } Adems cuando termina de emplear la zona de memoria asignada la libera y /* copia "Hola" a la cadena */ retorna 0. strcpy(cad, "Hola"); Es bastante comn que los programas /* muestra la cadena */ retornen 0 cuando no hay errores printf("cad es %s\n", cad); durante la ejecucin y otros enteros como cdigos de error. /* libera memoria */ Ahora podemos hacer programas que free(cad); pueden emplear diferentes cantidades de memoria segn sus necesidades en return 0; tiempo de ejecucin. Veamos el } ejemplo completo de la mediana de un conjunto de nmeros: 1. Planteamiento del Problema y Teora preliminar:

n Dados n nmeros reales, la mediana es el dato que quede en la posicin despus de ordenar 2 ascendentemente los n nmeros.
Por ejemplo la mediana de los 5 nmeros: 3,2,5,9,2 es 3. 3,2,5,9,2 Ordena 2,2,3,5,9 3er dato 3

2. Macro Algoritmo a) Pedir cantidad de datos y datos (se localiza memoria segn la cantidad de datos) b) Ordenar los datos suministrados c) Extraer el dato de la mitad y mostrarlo como resultado. 3. Especificacin de funciones
void Recibe(int n, float vec[]); /* PRE: Recibe la cantidad de datos (n) y un vector con espacio suficiente para n datos flotantes */ /* POST: Llena el vector vec, con datos suministrados por el usuario */ void Ordena(int n, float vec[]); /* PRE: Recibe la cantidad de datos (n) y un vector con n flotantes */

/* POST: Modifica el vector vec para que tenga los datos originales pero ordenados ascendentemente */

4. Implementacin
#include <stdio.h> #include <alloc.h> void inter_float(float *x,float *y); void Ordena(int n, float vec[]); void Recibe(int n,float vec[]); int main() { int n; float *vf; printf("Calculo de Mediana\n" "Cuantos datos ? "); scanf("%i",&n); vf=(float *)malloc(n*sizeof(float)); if (vf==NULL) { printf("No hay suficiente memoria\n"); return 1; } Recibe(n,vf); Ordena(n,vf); printf("La mediana es: %f\n",vf[n/2]); free(vf); return 0; } void inter_float(float *x,float *y) { float t; t=*x; *x=*y; *y=t; } void Ordena(int n, float vec[]) { int i; float t; for (i=0;i<n-1;) { if (vec[i]>vec[i+1]) { inter_float(vec+i,vec+i+1); i=0; } else { i++; } } } void Recibe(int n,float vec[]) { int i; for (i=0;i<n;i++) { printf("Dato %i: ",i+1); scanf("%f",vec+i); } }

Del programa recin presentado se recomienda observar: Asigna slo la memoria necesaria para almacenar los datos y la libera cuando ya no requiere ms su contenido. Verifica que malloc haya podido localizar la memoria solicitada. En caso de que no hubiese podido muestra un mensaje y retorna un cdigo de error. Emplea la funcin inter_float que intercambia el valor de dos variables de tipo flotante. Las funciones Ordena y Recibe reciben un vector de flotantes como parmetro, aunque desde el main se est pasando un apuntador. La invocacin de las funciones scanf en Recibe e inter_float en Ordena, emplea suma de apuntadores con enteros El ordenamiento se hace por el algoritmo de burbuja.

9.4.4 Vectores de apuntadores Como los apuntadores son tipos, pueden hacerse vectores de apuntadores. (No confundir con un apuntador a un vector). Una aplicacin tpica es una lista de nombres, dado que cada nombre es una cadena y las cadenas son vectores que pueden ser referenciados por medio de apuntadores. Veamos grficamente un vector de apuntadores a vectores:
vap

'J' 'u' 'a' 'n' '\0' La declaracin de vap es: 'A' 'n' 'a' '\0'
char *vap[4];

0 1 2 3

'P' 'e' 'd' 'r' ''o' '\0' 'M' 'a' 'r' 'i' 'a' '\0'

Como es un arreglo, podemos inicializarlo durante la declaracin de la siguiente forma:


char *vap[4]=

{"Juan","Ana","Pedro","Maria"};

O an ms breve:

char *vap[]={"Juan","Ana","Pedro","Maria"};

Una vez se haya inicializado de esta forma el vector vap se tendr:


printf("%c",*vap[0]); /* Imprime el caracter 'J' */ printf("%s",vap[2]); /* Imprime la cadena "Pedro"*/ printf("%c",*(*(vap+2)+3)); /* Imprime el caracter 'r' */

Una estructura similar pudo haberse logrado con una matriz de caracteres, en la que se colocar en cada fila una matriz. La diferencia es que tendra que haberse dado desde el comienzo una cantidad de columnas fijo, igual al tamao de la cadena ms grande, adems su inicializacin sera ms dispendiosa:
char mcar[4][6]="{{'J','u','a','n','\0',' '}, {'A','n','a','\0',' ',' '}, {'P','e','d','r','o','\0'}, {'M','a','r','i','a'}};

Adems no podra usarse el identificador de formato %s en un printf para imprimir cadenas.


printf("%c",mcar[2][2]); /* Imprime 'd' */

Para imprimir un nombre debemos usar un ciclo:


int y,x; for (y=1,x=0;mcar[y][x];x++) { printf("%c",mcar[y][x]); } /*Imprime "Ana" */

Ahora se desarrollar una aplicacin completa que emplea un vector de apuntadores y ejemplifica los conceptos estudiados en todo el captulo, se trata del programa SORT. 1. Planteamiento del problema El programa SORT del DOS (y tambin de UNIX) recibe del usuario una lista de cadenas, y despus se las muestra ordenadas de menor a mayor. 2. Macro Algoritmo a) Pedir al usuario la cantidad de cadenas a ordenar b) Localizar memoria para el vector de cadenas c) Pedir al usuario las cadenas por ordenar y para cada una localizar memoria y ponerla en el vector d) Ordenar las cadenas recibidas e) Mostrar por pantallas las cadenas ordenadas f) Liberar toda la memoria solicitada 3. Especificacin de funciones
void PideCadenas(int n,char *vc[]); /* PRE: Recibe la cantidad de cadenas a pedir y un vector para apuntarlas */ /* POST: Por cada cadena dada por el usuario se localiza memoria suficiente y se apunta con un elemento del vector. Puede retornar 0 si no hay error y 1 si no hay memoria suficiente */ void Ordena(int n,char *vc[]); /* PRE: Recibe la cantidad de cadenas y el vector vc con apuntadores a tales cadenas */ /* POST: Reorganiza los apuntadores del vector vc para que las cadenas a las que apuntan quede ordenadas alfabeticamente */ void MuestraCadenas(int n,char *vc[]);

/* PRE: Recibe la cantidad de cadenas (n) y el vector vc con apuntadores a tales cadenas */ /* POST: Muestra las cadenas apuntadas por los elementos de vc, desde vc[0] hast vc[n-1]. */

5. Implementacin La funcin scanf, cuando tiene el especificador de formato %s, espera del usuario una palabra (sin espacios) y la coloca en la cadena que recibe, como en este caso necesitamos que espere una lnea entera del usuario y la almacene en una cadena, empleamos la funcin gets definida en stdio.h, su especificacin es la siguiente:
char *gets(char *s); /* PRE: Recibe una cadena s, con suficiente espacio para la linea que ingresar el usuario */ /* POST: Llena el vector s, con los caractres de la linea dada por el usuario (terminada con '\n') y retorna un apuntador a tal linea (s). Si encuentra un fin de archivo (EOF) retorna NULL*/

Adems no es conveniente mezclar la funcin scanf con gets, pues puede generar comportamientos a primera vista extraos. Por tal motivo el primer entero solicitado se pide como una lnea y despus se convierte a entero empleando la funcin atoi, declarada en la librera stdlib.h y cuya declaracin y especificacin se presentan a continuacin:
int atoi(const char *s); /* PRE: Recibe una cadena s */ /* POST: Convierte la cadena s a entero y lo retorna. Asume que la cadena s se compone de: 1. Una cadena opcional de espacios o tabuladores, 2. Un signo opcional (+/-), 3. Una cadena de digitos. no puede completar la conversin retorna 0*/

Si

La implementacin se muestra en las pginas siguientes.

6. Inconvenientes El programa mostrado, compara cadenas teniendo en cuenta capitalizacin (maysculas y minsculas). e.g. La cadena "Zimbawe" es menor que "altura". En realidad el programa SORT, no pregunta al usuario la cantidad de lneas a ordenar, sino que empieza a leer lneas hasta que el usuario da el comando fin de archivo o EOF. (En un IBM PC, bajo sistema operativo DOS, se produce presionando la tecla F6 , en una mquina UNIX presionando [Control]-[D]) Usa la funcin fgets que no es segura. Emplea un algoritmo de ordanamiento poco eficiente.

#include #include #include #include

<stdlib.h> <stdio.h> <alloc.h> <string.h> /* Tamao mximo que puede tener una linea */

#define MAX_TAM_LINEA 100

int Halla_Max(int n,char *cad[]); /* Usado por la funcion de ordenar */ void MuestraCadenas(int n,char *vc[]); void Ordena(int n,char *vc[]); int PideCadenas(int n,char *vc[]); int main() { int n; /* Nmero de lineas */ char **Lineas; /* Apuntar al vector de apuntadores */ /*??? Porqe debe ser un doble apuntador ??? */ int i; char lin[MAX_TAM_LINEA]; printf("SORT\nOrdena lineas\n\nCuantas lineas va a ordenar? "); gets(lin); n=atoi(lin); if (n<1) { printf("Nmero de lineas no valido \n"); return 2; } /* Localizamos memoria para el vector */ Lineas=(char **)malloc(sizeof(char *)*n); /*??? Porqe debe se hace un casting a char ** ??? */ /*??? Porqe se toma el tamao de un char * ??? */ if (Lineas==NULL) { printf("Memoria insuficiente\n"); return 1; } if (PideCadenas(n,Lineas)) { /* ??? Porque la condicin de este if no tiene operadores de comparacin ???*/ printf("Memoria insuficiente\n"); return 1; } Ordena(n,Lineas); printf("\n-----------------------------------------------\n"); MuestraCadenas(n,Lineas); printf("\n-----------------------------------------------\n"); /* Ahora liberamos memoria reservada en PideCadenas */ for (i=0;i<n;i++) { free(Lineas[i]); } free(Lineas); return 0; } /* Liberamos tambin el vector de apuntadores */

int Halla_Max(int n,char *cad[]) { /* PRE: Recibe la cantidad de cadenas (n) y el vector vc con apuntadores a tales cadenas */ /* POST: Retorna la posicin dentro del vector que apunta a la mayor cadena encontrada */ char *max; /* Apuntador a la cadena ms grande encontrada */ int pmax; /* Posicin dentro del vecto a la cadena ms grande */ int i; for (i=1,pmax=0,max=cad[0];i<n;i++) { /* ??? Porque i se inicializa en 1 y no en 0 ???*/ if (strcmp(cad[i],max)>0) { /*??? Porque aqui no dijo simplemente max>cad[i] ??? */ /*??? Que ocurrira si dijera strcmp(max,cad[i]) ??? */ max=cad[i]; pmax=i; } } return pmax; }

void MuestraCadenas(int n,char *vc[]) { /* PRE: Recibe la cantidad de cadenas (n) y el vector vc con apuntadores a tales cadenas */ /* POST: Muestra las cadenas apuntadas por los elementos de vc, desde vc[0] hast vc[n-1] */ int i; for (i=0;i<n;i++) { printf("%s\n",vc[i]); } }

void Ordena(int n,char *vc[]) { /* PRE: Recibe la cantidad de cadenas y el vector vc con apuntadores a tales cadenas */ /* POST: Reorganiza los apuntadores del vector vc para que las cadenas a las que apuntan quede ordenadas alfabeticamente */ int i; int pmax; char *t; for (i=n;i>1;i--) { pmax=Halla_Max(i,vc); t=vc[pmax]; vc[pmax]=vc[i-1]; vc[i-1]=t; } }

int PideCadenas(int n,char *vc[]) { /* PRE: Recibe la cantidad de cadenas a pedir y un vector para apuntarlas */ /* POST: Por cada cadena dada por el usuario se localiza memoria suficiente y se apunta con un elemento del vector. Si tiene exito retorna 0, Si se agota la memoria retorna 1 */ /*??? Porque es importante que PideCadenas retorne un entero? */ int i; char linea[MAX_TAM_LINEA]; /*??? Para que se usa la variable linea aqui, en vez de usar directamente el vector recibido */ for (i=0;i<n;i++) { printf("Teclee la linea %i:\n",i+1); /*??? Porque se imprime i+1 y no i ???*/ gets(linea); vc[i]=(char *)malloc(sizeof(char)*(strlen(linea)+1)); /*??? Porque es importante aqui strlen(linea)+1 ???*/ if (vc[i]==NULL) { return 1; } strcpy(vc[i],linea); } return 0; }

Argumentos de un programa Hasta el momento se ha trabajado con una funcin main que no recibe argumentos y retorna un entero:
int main()

En realidad la funcin main, si puede tener parmetros, correspondern a los argumentos pasados en la lnea de comandos al invocar el programa. Por ejemplo la primitiva copy en DOS (programa cp en UNIX) encargada de copiar archivos, recibe dos argumentos, el primero es el nombre del archivo fuente y el segundo el nombre del archivo destino. Cuando se invoca un programa escrito en C, estos argumentos quedarn organizados en un vector de cadenas, este vector siempre se identifica con el parmetro argv , la cantidad de parmetros recibidos queda en una variable de tipo entera que puede recibir el main como argc. La definicin del main, cuando se van a recibir los parmetros deber comenzar as:
int main(int argc,char *argv[])

Cada cadena de argv, ser un parmetro de la lnea de comandos, el primero es el nombre del programa (por est razn argc, siempre es mnimo 1) por ejemplo el comando
echo Esta es una prueba

tanto en DOS como en UNIX imprimir el argc mensaje "Esta es una prueba". Si echo fuese un 5 programa escrito en C, los parmetros argc y argv sern como se muestran en la figura. Una vez dentro del main, podrn usarse los argumentos recibidos, como se ejemplifica a en el siguiente programa que imprime todos los argumentos que recibe:

argv "Echo" "Esta" "es" "una" "prueba"

#include <stdio.h> int main(int argc,char *argv[]) { int i; for (i=0;i<argc;i++) { printf("%i: %s \n",i,argv[i]); } return 0; }

*9.5. Apuntadores a funciones PREGUNTAS 1. Por que la funcin scanf (librera stdio.h) recibe como parmetros apuntadores? Que ocurrira si en vez de esto recibiera valores? 2. Que valor adquiere la variable i, una vez se ejecuta la siguiente porcin de programa?

int i; char c; A (int)c;

3. Suponga que el siguiente programa se ejecuta en una IBM PC:


int a; long b=800000000; a=(int)b;

a. Una vez se ejecute esta porcin de cdigo, la variable a tendr el valor 800000000? (Ayuda: Mire el rango de valores que puede adquirir una variable tipo int en un PC) b. Explique en que casos el casting preserva el valor y en cuales no. 4. La funcin strchr mostrada en uno de los ejemplos de la seccin 9.4.3, puede ser an ms breve, aprovechando que el ASCII de \0 es 0. Escriba nuevamente esta funcin aprovechando esto. (Ayuda: Puede aplicarse 2 veces) 5. Cual es el estado de las variables cuando se termina de ejecutar la porcin de cdigo del ltimo ejemplo de la seccin 9.4.2 ? 6. Haga diagramas de memoria para la porcin de programa del primer ejemplo de la seccin 9.4.3. Explique cual es el problema que tiene ese programa al ejecutarse. 7. El siguiente programa tiene un problema. Cual es?
int *i; (int *)malloc(sizeof(int)); free(i); printf ("El valor de *i es %i",*i);

8. Haga diagramas de memoria para mostrar lo que ocurre al ejecutar el ejemplo completo de la seccin 9.4.3.3 9. Porque cree que a una compaa de software le interesan lenguajes de programacin portables? 10.Declare un apuntador a flotantes, y emplelo para apuntar a un vector de 10 flotantes localizado dinmicamente con malloc. Llene este vector con los flotantes 0.0, 1.1, 2.2, ..., 9.9. Finalmente imprima el contenido de todo el vector y libere la memoria que emple. 11.En el ejemplo de la mediana de la seccin 9.4.3, la funcin Ordena, hace la invocacin: inter_float(vec+i,vec+i+1), que ocurre si cambiamos esa invocacin por: inter_float(vec[i],vec[i+1]) ? 12.Defina un vector de apuntadores a vectores de caracteres (o ms breve un vector de cadenas), con las siguientes cadenas (en este orden): "El", "Lenguaje", "de", "Programacin", "C". Tambin defina una matriz de caracteres con las mismas cadenas. 13.Que ventajas tiene un vector de cadenas (Ejemplo 1 seccin 9.4.4) sobre una matriz de caracteres? Piense por ejemplo en espacio requerido por cada uno y facilidad de uso.

14.Haga la funcin atoi cuya especificacin se presenta en el ejemplo SORT de la seccin 9.4.4. 15.En el cdigo fuente del programa SORT se presentan varias lneas de comentarios, comenzadas con /*???. Son preguntas referentes al programa, contstelas todas.

9.6. EJERCICIOS Seccin 9.4.1. 1. Se requiere una funcin que reciba 3 enteros y retorne los 3 enteros ordenados de menor a mayor. 2. Haga la funcin Halla_Max, que recibe un vector de enteros y el tamao de tal vector. Retorna la posicin del mximo y el mximo elemento del vector. (Ayuda: Puede retornar uno o ambos valores en parmetros pasados por referencia) Seccin 9.4.2. 1. Haga la versin con apuntadores de la funcin strcat de la librera string.h. La siguiente especificacin fue tomada del Help del BC ++ 3.1 Agrega una cadena al final de otra Declaracin:
char *strcat(char *dest, const char *src);

Comentarios: strcat concatena una copia de src al final de dest. La longitud de la cadena resultante es strlen(dest) + strlen(src). Valor retornado: strcat retorna un apuntador a las cadenas concatenadas (es decir a src) 2. Haga la versin con apuntadores de la funcin strchr de la librera string.h. La siguiente especificacin fue tomada del Help del BC ++ 3.1. Mire con cuidado los comentarios y notar que la funcin presentada en este captulo no cumple todos los requerimientos. Busca en una cadena la primera ocurrencia de un caracter dado Declaracin:
char *strchr(char *s, int c);

Comentarios: strchr hace un recorrido hacia adelante, buscando el caracter especificado. Encuentra la primera ocurrencia del caracter c en la cadena s. El caracter de fin de cadena (\0) es considerado como parte de la cadena; por ejemplo, strchr (strs,0) retorna un apuntador al caracter de fin de la cadena strs. Valor retornado: Si tiene xito, retorna un apuntador a la primera ocurrencia del caracter c en la cadena s. En caso de error (si c no ocurre en s) retorna NULL. 3. Haga la versin con apuntadores de la funcin strcmp de la librera string.h. La siguiente especificacin fue tomada del Help del BC ++ 3.1. Compara dos cadenas Declaracin:
int strcmp(char *s1,char*s2);

Comentarios: strcmp hace una comparacin sin signo de las cadenas s1 y s2. La comparacin de las cadenas comienza con el primer carcter de cada cadena y continua con los siguientes hasta que los caracteres correspondientes difieran o hasta que termine se alcance el final de la cadena. Valor retornado: Esta rutina retorna un valor de tipo int, que cumple: < 0 si s1<s2 si s1>s2 == 0 si s1==s2 4. Haga la versin con apuntadores de la funcin strcpy de la librera string.h. La siguiente especificacin fue tomada del Help del BC ++ 3.1. Copia una cadena fuente en una destino Declaracin:
char *strcpy(char *dest, char *src);

Comentarios: Copia la cadena src en dest, detenindose despus de haber copiado el caracter de terminacin de cadena. Valor retornado: Apuntador a la cadena destino dest 5. Haga la versin con apuntadores de la funcin strlen de la librera string.h. La siguiente especificacin fue adaptada del Help del BC ++ 3.1. Calcula la longitud de una cadena Declaracin:
int *strlen(char *s);

Comentarios: Calcula la longitud de la cadena s Valor retornado: Retorna el nmero de caracteres de la cadena s, sin contar el caracter de fin de cadena.

6. Haga la versin con apuntadores de la funcin strlwr de la librera string.h. La siguiente especificacin fue tomada del Help del BC ++ 3.1. Convierte una cadena a minsculas Declaracin:
char *strlwr(char *s);

Comentarios: Convierte las letras maysculas (A - Z) de la cadena s a letras minsculas (a - z). Valor retornado: Un apuntador a la cadena s

Seccin 9.4.4. 1. Haga la funcin Ordena del programa ejemplo SORT, pero empleando el algoritmo de ordenacin por burbuja. 2. Haga los cambios necesarios al programa SORT para que no distinga mayusculas de minusculas.

3. Modifique el programa SORT, de forma que no pida la cantidad de cadenas al comienzo, sino que pida lineas al usuario hasta que l envie un fin de archivo EOF. (Ayuda: La funcin gets retorna NULL cuando esto ocurre) 4. Haga un programa de nombre TESTARG que imprima en pantalla el mensaje "Probando" cuando se invoque como:
TESTARG -test

y la cadena "Parmetro invalido" en otro caso.

9.7. PROYECTOS 1. Existen programas que permiten obtener estadsticas de datos, (SPSS es uno de los ms famosos). Inclusive las calculadoras sencillas comnmente traen algunas funciones de estadstica. Haga un programa que permita: 1. Editar los datos por analizar (el nmero de datos debe ser variable) 2. Calcular promedio 3. Calcular mediana 4. Calcular desviacin estndar 5. Calcular error cuadrtico medio Como restriccin, debe mantener los datos en un vector que se localice dinmicamente. 2. Haga un rograma que cuente la frecuencia de las palabras provenientes de la entrada estndar. Por ejemplo si el usuario tecleara: Solo se que nada se wc retornara una lista como Solo 1 se 2 que 1 nada 1 Haga este programa, de forma que espere lneas provenientes de la entrada estndar hasta que llegue un caracter fin de archivo, y muestre por la salida estndar una lista de las palabras del texto de entrada, junto con su frecuencia, sin tener en cuenta capitalizacin ni signos de puntuacin. Debe emplear un vector de apuntadores para mantener las palabras digitadas por el usuario y otro para mantener la frecuencia. Opcionalmente la salida puede estar ordenada alfabticamente. BIBLIOGRAFA [1] BORLAND C++ 5.0. Programmers Guide. 1996 [2] KERNIGHAN, Brian. RITCHIE, Dennis M. El lenguaje de programacin C. Prentice Hall. 1985 [3] DEITEL, H. M. DEITEL, P.J. C How to Program. Editorial Prentice Hall. Editorial

1992.

[4] MILENKOVIC, Milan.Sistemas Operativos: Conceptos y diseo. Mc Graw Hill. 1994. CREDITOS, PROPIEDAD INTELECTUAL: vtamara@users.sourceforge.net Dominio pblico. 2004. Sin garantas.

Вам также может понравиться