9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que...

27
9. APUNTADORES EN C 9.1. MOTIVACIÓN 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 información 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 sería un vector de flotantes. Cuantos elementos debe tener el vector? 30? 50?. No faltará el práctico 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 tamaño en tiempo de ejecución (es decir mientras el usuario final esta empleando el programa), en el ejemplo anterior, puede emplearse un apuntador y memoria dinámica para crear un vector con tantas posiciones como lo desee el usuario final. Los apuntadores también facilitarán la creación y mantenimiento de estructuras mucho más complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación de apuntadores será permitir variables por referencia en las funciones. Es decir permiten que una función modifique el contenido de las variables que recibe como parámetros. Además dada la intima relación entre vectores y apuntadores, las operaciones con vectores, como se verá, pueden también hacerse con apuntadores y en muchos casos con más facilidad. La total comprensión del uso de apuntadores requiere un conocimiento más profundo del hardware y del sistema operativo, la sección 9.2. está dedicada a tratar algunos conceptos claves de estos temas. Despúes en la sección 9.3. se tratará la sintaxis para declarar y usar apuntadores y se explicará su semántica. Finalmente en la sección 9.4. se pondrá en práctica la teoría, y se darán varias aplicaciones, entre ellas el uso de memoria dinamica.

Transcript of 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que...

Page 1: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

9. APUNTADORES EN C

9.1. MOTIVACIÓN

Cuando nos enfrentamos a un problema que tenemos planeado resolver con un programa escritoen C, empezamos modelando dicho problema (i.e. haciendo explicitos cuales son los datos deentrada y salida del problema y como los representaremos en el computador). Este modelaje,hasta el momento a supuesto que el tipo de información y la cantidad de está, es conocida. Esteno es el caso en muchos programas, por ejemplo en un programa para calcular la mediana de lascalificaciones de un curso, se requieren todas las calificaciones; la cantidad de calificacionesdependen de la cantidad de estudiantes y este puede ser un dato desconocido al desarrollar elprograma. Muy probablemente la estructura de datos escogida para almacenar las calificacionessería un vector de flotantes. Cuantos elementos debe tener el vector? 30? 50?. No faltará elpráctico que declare un vector de 1000 posiciones, pero si tal programa se emplea para un cursode 15 estudiantes, habrá 985 posiciones del vector desperdiciadas.

Una de las aplicaciones de los apuntadores es permitir manejar estructuras que pueden cambiar detamaño en tiempo de ejecución (es decir mientras el usuario final esta empleando el programa),en el ejemplo anterior, puede emplearse un apuntador y memoria dinámica para crear un vectorcon tantas posiciones como lo desee el usuario final.Los apuntadores también facilitarán la creación y mantenimiento de estructuras mucho máscomplejas que vectores o matrices, como es el caso de listas, arboles y grafos.Otra aplicación de apuntadores será permitir variables por referencia en las funciones. Es decirpermiten que una función modifique el contenido de las variables que recibe como parámetros.Además dada la intima relación entre vectores y apuntadores, las operaciones con vectores, comose verá, pueden también hacerse con apuntadores y en muchos casos con más facilidad.

La total comprensión del uso de apuntadores requiere un conocimiento más profundo delhardware y del sistema operativo, la sección 9.2. está dedicada a tratar algunos conceptos clavesde estos temas. Despúes en la sección 9.3. se tratará la sintaxis para declarar y usar apuntadores yse explicará su semántica. Finalmente en la sección 9.4. se pondrá en práctica la teoría, y sedarán varias aplicaciones, entre ellas el uso de memoria dinamica.

Page 2: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

9.2 LAS VARIABLES EN LA MÁQUINA

Para comprender que es y como se usa un apuntador es útil comprender como se representan lasvariables en la memoria del computador.

Primero que todo recordemos que la memoria del computadorpuede representarse como un conjunto de celdas (o mejor un vectorde celdas), cada una de las cuales puede almacenar un byte1.

Para el caso mostrado en la Figura 1 se tiene una memoria quepodrá almacenar m bytes

Supongamos que hacemos el siguiente programa:

Cuando se ejecute este programa, se creará una zona de memoria dedicada a la variable a, en lafigura 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 zonareservada para a, y cuando se extraiga elvalor de a, se sacará el dato almacenadoen esa zona.

Ya es más claro porque las variablesdeben ser inicializadas siempre?Que había en la zona de la variable aantes de que esta fuese reservada? Nopodemos saberlo, podría ser 0, 8, 1043,cualquier número dejado allí por otros

programas, o por el sistema operativo.

El programa de ejemplo, después llenará el espacio reservado para a, con el valor 0, como semuestra en la figura 2.2. Y finalmente recuperará el contenido de la variable a (es decir lainformación que hay en la zona de a) y lo retornará. Como el valor contenido en la zona de a es0, retornará 0.

1 Un byte es una agrupación de 8 bits. Un bit puede pensarse como un 0 o un 1.2 Vale la pena aclarar que las direcciones de memoria no son necesariamente números enteros. La expresión i+1, se

refiere a la posición de memoria que le sigue a la posición i.

01

. . .

m

Figura 1

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

0 0 1 1 . .i 75 Zona i 0i+1 23 de a i+1 0

.

.m m Figura 2.1. Figura 2.2.

Page 3: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

Ahora surgen nuevas preguntas:

• ¿ Como se escoge el sitio donde quedarácada variable en memoria?

• ¿ De que tamaño es la zona de memoriareservada?

• ¿ Si el computador sólo trabaja con unosy ceros como se puede trabajar convariables de tipo entero, flotante, caráctery vector?

• ¿ Como se asegura que la zona dememoria reservada para una variable noserá escrita por otra variable delprograma o por el programa mismo?

9.2.1. Representación de datos

Ya sabemos que las variables son en realidad zonas de memoria, asignar un valor a una variableequivale a llenar con el valor dado la zona de la variable, y recuperar el valor de una variableequivale a sacarlo de la zona de memoria.

Ahora se tratará de responder la pregunta: ¿Como almacena el computador un entero, un flotante,un carácter? 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 altoy otro el nivel bajo, al nivel alto de voltaje se le asocia con el 1 y al nivel bajo con el 0. Lasmemorias de los computadores se basan en circuitos que pueden almacenar uno de estos nivelesde voltaje3 (es decir pueden almacenar un bit). Así mismo toda la transmisión de información porlos buses y cables, se presenta como señales de voltaje (nivel alto y nivel bajo). En resumen todala información que puede trabajar un computador debe en últimas representarse como secuenciasde unos (1) y ceros (0).

Como sólo tenemos disponibles los dígitos 0 y 1, para representar la información empleamosnúmeros en base 2. Por ejemplo el entero 6 (escrito en base 10), se escribe como 110 en base 2.Esta secuencia de dígitos (110) si puede ser almacenada en la memoria del computadorempleando 3 flip-flops o su equivalente: El primero almacena un nivel alto de voltaje, el segundoun nivel alto, y el tercero un nivel bajo.

Para representar caracteres se emplea la tabla ASCII (Ver el Apendice B), de forma que todocarácter se representa como un número entero y este a su vez se expresa en base 2. Por ejemploel valor ASCII asociado a la letra ‘C’ es 67, entonces en el computador se almacenará como lasecuencia de bits: 1000011.

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

Nota Histórica

Cuando comenzó el auge de los computadorespersonales, a principios de la década de los 80,eran comunes computadores cuyas memoriasno sobrepasaban los 64 KB (1KB equivale a1024 bytes), tal era el caso del ZX Spectrum,Commodore 64, Color Computer I y II, etc.El procesador 8088 de Intel permitíadireccionar hasta 1 MB de memoria (1MBequivale a 1024KB). Esto fue aprovechadopor IBM que lanzó sus primeros PC con512KB en RAM. Tiempo después aparecieronnuevos procesadores para microcomputadores,que permitían direccionar más memoria.

Page 4: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

Ya se explico como se representan los enteros positivos. Para representar enteros tanto positivoscomo negativos se emplea uno de los bits del número que es 0 si el signo es positivo y 1 si es unentero negativo (a este bit se le llama bit de signo). Además se trabaja en complemento a 2(puede consultar la bibliografía para saber en que consiste).

La representación de números flotantes es un poco más complicada, la mayoría de computadoresse han acogido a un estándar definido por la IEEE. Cada número flotante se escribe empleandonotación científica, como un dígito, con una expansión decimal truncada (es decir sólo se tomacierta cantidad de dígitos) y un exponente. (e.g. 54.32 se escribe como 5.432E-1). El lectorinteresado puede remitirse a la bibliografía.

9.2.2. Tamaño 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 tamaño de la zona reservada,depende del tipo de variable que hayamos declarado y del computador en el cual esteejecutándose el programa. Este tamaño reservado limita los valores que puede tomar la variable.Por ejemplo si se reservan 2 bytes para almacenar números enteros, podrá mantener secuenciasde a lo sumo 16 unos y ceros, es decir podrá representar a lo sumo 65536 valores diferentes (elprimero corresponde a la secuencia 0000000000000000 y el último corresponde a la secuencia1111111111111111).

Como se dijo este tamaño depende de la máquina en la que se este ejecutando el programa, delcompilador y del tipo, algunos tamaños típicos se listan en la tabla 14

Maquina int float char long int double

PC/BC++ 16 bits 32 bits 8 bits 32 bits 64 bitsDEC PDP-11 16 bits 32 bits 8 bits 32 bits 64 bitsHoneywell 6000 36 bits 36 bits 9 bits 36 bits 72 bitsIBM 370 32 bits 32 bits 8 bits 32 bits 64 bits

Tabla 1

9.2.3. Duración de variables en C

En los programas realizados hasta ahora se ha visto que las variables declaradas sólo existen alinterior del bloque donde fueron declaradas y sólo pueden usarse allí, excepto las variablesglobales cuya existencia se asegura a lo largo de todo el programa y pueden ser usada desdecualquier 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 propósito, queninguna otra parte del programa emplea, y que puede ser accedida por cualquier función delprograma. Por el contrario para las variables locales, se reserva una zona temporal que sóloexiste mientras que la función donde se declaró la variable esté ejecutándose.

4 Tomado de [2]. Pag.36

Page 5: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

Este comportamiento puede ser modificado empleando los prefijos static, auto y registeral declarar variables. Para conocer como afectan la duración de las variables, puede referirse a labibliografía.

PREGUNTAS

1. Escriba en base 2 los siguientes enteros (que están 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 carácter en una máquina IBM PC?4. Cuantos enteros distintos podrá representarse con una variable tipo long int en una máquina

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?

Page 6: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

9.3. ¿QUE ES UN APUNTADOR?

La discusión desarrollada en la sección 9.2 pretende dar algunas nociones básicas que permitiránentender con mayor facilidad el concepto de apuntador.

El ejemplo mostrado es un programa muy sencillo que ilustra eluso de apuntadores. Primero se declara una variable a de tipo enteroy una variable ap tipo apuntador a entero. En memoria se reserva unespacio para la variable a (supongamos que estamos trabajando enuna máquina 80x86 bajo DOS con BC++, entonces serán 2 bytes) yotro para el apuntador (en maquinas 80x86 bajo DOS con BC++serán 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 tieneasignados los bytes j, j+1,j+2 y j+3.

Cuando se ejecute la asignación a=0, las posiciones i e i+1 obtendrán el valor 0 (sus 16 bitsestarán en 0).

El operador & es un operador unario prefijo, que se aplicasobre variables y retorna la dirección de la variable que opera(esto es la posición en memoria donde comienza la zonareservada para la variable), por ejemplo &a será la dirección dela variable a, en el caso del ejemplo será i.

Las direcciones, no se consideran como enteros ni comoflotantes ni como caracteres en C. Por esta razón no puedeasignarse una dirección a una variable de alguno de estos tipos.En el programa de ejemplo se hace la asignación ap=&a, esdecir asignamos la dirección de a a la variable ap. Estaasignación si es valida porque ap está declarado comoapuntador a entero. Cuando se realice la asignación, el espacioen memoria de ap contendrá la dirección i.

La utilidad de los apuntadores es que permiten referirse directamente a direcciones de memoria.En nuestro ejemplo ap podrá referirse a la dirección i (que corresponde a la zona de memoria dela variable a). Para referirse a la dirección de memoria contenida en un apuntador se usa eloperador unario prefijo *. Así en la asignación *ap=1, estamos asignando el entero 1 a ladirección apuntada por ap, es decir asignamos 1 a la zona de memoria iniciada en la dirección i.Como efecto de esta operación el valor de la variable a cambia de 0 a 1 (precisamente porquemodificamos la zona reservada a la variable a).

Cuando se ejecute la instrucción return a, el programa terminará y retornará el valor 1.

intmain () { int a; int *ap; a=0; ap=&a; *ap=1;

return a;}

0 1 . .i a

.

j . ap

m

Figura 3.1.

Page 7: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

9.3.1. Declaración de apuntadores

La declaración de apuntadores puede realizarse en cualquier parte del programa donde sea validala declaración de variables. La sintaxis es:

<tipo> *<identificador>;

Donde <tipo> es un tipo valido en C (e.g. int, long int, double, etc.), e<identificador> es un nombre de variable válido.

<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 carácter, esto significa que contendrá la direccióndonde comienza un carácter. La variable ap_a_numerote podrá contener la dirección dondecomienza un entero largo sin signo.

9.3.2. Asignación de apuntadores

A una variable de tipo apuntador sólo pueden asignársele una dirección de memoria. Para obtenerla dirección de una variable (inclusive de un apuntador), se emplea el operador & a la izquierdadel nombre de la variable.5

El operador & sólo 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 dirección de una variable no puede ser asignada, el siguiente ejemplo muestra que NO esvalido con el operador &:

int a,b;

&a=&b;

A una variable tipo apuntador puede asignársele el valor de otra variable del mismo tipo como seejemplifica a continuación:

double d;double *pd1;double *pd2;

pd1=&d;

5 También puede asignarse otras direcciones de memoria, pero debe conocerse el modo de direccionamiento de lamáquina y tener grandes precauciones en el uso de tales apuntadores.

Page 8: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

pd2=pd1;

El operador & tiene mayor precedencia que los operadores aritméticos.

A una variable de tipo apuntador, sólo pueden asignársele direcciones de memoria, la únicaexcepción es el entero 0. El valor 0 es tratado de forma especial con apuntadores, cuando se leasigna 0 a un apuntador estamos diciendo que tal variable no está apuntando a zona alguna dememoria.

En la librería stddef.h se declara la constante NULL con el valor 0, este es el símbolo queemplearemos para referirnos a apuntadores que no están direccionando alguna zona de memoria.A continuación 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 posición de memoria contenida en un apuntador se usa el operador * a laizquierda del apuntador.A diferencia del operador &, este operador si puede colocarse al lado izquierdo de una asignación(así como al lado derecho). Por ejemplo:

char c;char *pc;

pc=&c;*pc=‘c’;c=*pc+2;

El operador * sólo puede aplicarse a variables de tipo apuntador. El siguiente ejemplo muestraun empleo incorrecto de este operador

int c;*c=0;

Este operador tiene mayor precedencia que los operadores aritméticos.

9.3.4. Operaciones entre apuntadores

Ya se explico que no pueden asignarse enteros a variables de tipo apuntador, ni direcciones avariables de tipo entero. Aún así hay varias operaciones en las que pueden emplearse enteros yapuntadores, estas son adición (+) y resta (-).Veamos un ejemplo

Page 9: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

En este ejemplo se declaran dos apuntadores a flotantes, un vectorpara 5 flotantes y un entero.Primero se asigna la dirección del comienzo del vector f alapuntador apf1 .(la asignación también pudo haberse hecho conapf1=f;) Después esa misma dirección es asignada a la variableapf2.

Analicemos ahora la asignación apf1=apf1+3. La expresiónapf1+3 será la dirección de apf1 incrementada en tres posiciones

(teniendo en cuenta que apf1 apunta a flotantes), es decir equivale a la dirección del cuartoflotante del vector f (porque apf1 apunta al primero). La nueva dirección así calculada seasigna a apf1.

En la instrucción apf2=apf1-2, asignamos a la variable apf2 la dirección del segundo flotantedel vector f.

Finalmente en la instrucción d=apf1-apf2 obtenemos la distancia (en flotantes) entre losapuntadores apf1 y apf2, esto es la cantidad de flotantes que pueden ponerse entre ambasdirecciones, para este caso particular d obtendrá el valor 2.

Por ahora no es tan importante que comprenda el ejemplo en su totalidad pero será convenienteque vuelva a este ejemplo después de ver la utilidad de los operadores + y - al trabajar convectores.

9.3.5. Representación gráfica de apuntadores

Para referirnos a vectores, es usualmente más cómodo emplear una gráfica como la siguiente:

Cuando se trata de apuntadores también suele emplearse una representación gráfica, se trata deuna celda de la cual parte una flecha. El otro extremo de la flecha se ubica en otra celda, la cualrepresenta la variable (o zona de memoria) que está siendo apuntada.

Para ejemplificar como se usa esta representación gráfica retomaremos el ejemplo 1 de la sección9.3. Para cada operación importante del programa (con respecto a las variables a y ap) se hacenlos diagramas correspondientes.

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

v1: . . . 0 1 2 n-1

intmain () { int a; int *ap; a=0; ap=&a; *ap=1;

return a;}

2. 0 ap a

1. ap a

3. 0 ap a

4. 1 ap a

float f[5];float *apf1;float *apf2;int d;

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

Page 10: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

PREGUNTAS

1. Supongamos que a y b son variables de tipo entero, cual será el valor final de b después deejecutar las siguientes instrucciones?

a=a*a;b=*(&a);

2. La siguiente declaración es valida:int **app;

¿De que tipo es la variable app? ¿Como puede asignarse un valor a esta variable? ¿Cómo puedereferenciarse memoria con ella?3. ¿Qué función conoce que reciba un apuntador como uno de sus parámetros? (Ayuda: repase

las funciones de la librería stdio.h) ¿Cómo cree que es el encabezado (prototipo) de esafunción?

4. En el ejemplo de la sección 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 programade ejemplo de la sección 9.3.4.

7. ¿Porque cree que se considera peligroso el uso de apuntadores? ¿Que puede ocurrir con unapuntador no inicializado?

8. Como se representa gráficamente el apuntador a apuntador del ejercicio 2?

Page 11: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

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 parámetros del encabezado de la función, se pasanvariables por referencia). En C por el contrario siempre se pasan parámetros por valor, es decirque una función no puede modificar globalmente el valor de sus parámetros. Los cambios quehaga a sus parámetros, sólo serán locales a la función, una vez, esta termine y la funciónllamadora retome el control, el valor de los argumentos pasados será el mismo.

Sin embargo muchas vecesrequerimos que una funciónmodifique el valor de sus parámetrosen el resto del programa. Losapuntadores dan un medio para lograresto: podemos hacer funciones quereciban como parámetros apuntadoresa los valores y no los valores mismos.Una función que requiere modificarsus parámetros, es la función queintercambia el valor de dos variables.No podemos hacer una función quereciba dos enteros e intercambie susvalores, pero si una que reciba dosapuntadores a enteros e intercambielos valores referenciados por losapuntadores.

Vale la pena aclarar que losapuntadores son tratados como tipos en C, por esta razón es posible declarar funciones queretornen apuntadores.

9.4.2. Vectores y apuntadores

Los vectores en memoria son zonas de memoria consecutiva. Así un vector de 20 flotantes, enmemoria corresponde a una zona de memoria donde hay espacio para 20 flotantes (en el caso delIBM PC, esto equivale a 20*4 = 80 bytes).

Un vector, como buena zona de memoria, tiene una dirección donde comienza, es posible tenerun apuntador con tal dirección. Aún más es posible referenciar por medio de apuntadores,cualquier posición del vector.

Estudiemos en detalle el ejemplo presentado: Primero reservamos espacio para un contador(cont), una zona capaz de almacenar 20 flotantes de doble precisión consecutivos (vec), y unapara almacenar una dirección (pvec). Después a pvec le asignamos la dirección de la primera

#include <stdio.h>

void intercambia(int *x, int *y);/* Intercambia los valores de x e y */

intmain () { 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;}

Page 12: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

posición del vector vec (esto es la posición cuyo índicees 0). El estado de las variables puede representarsegráficamente así:

La variable cont es inicializada en 0, y entramos en un ciclo que se repetirá 20 veces. En cadaiteración le asignamos al número apuntado por pvec el valor de cont. Note que para estaasignación hacemos un casting o conversión de tipo. Convertimos el valor de tipo intalmacenado en cont, al mismo valor pero con tipo double. Empecemos con la primera iteración: cont tiene el valor 0, entonces en la posición 0 de vecalmacenamos un 0, después incrementamos el valor de cont (queda en 1), y así mismoincrementamos en uno el valor de pvec. Este incremento, como se explicó en la sección 9.3.4,hace que la nueva dirección que pvec contiene sea la de un dato delante del que apuntaba, esdecir la del double siguiente. En este caso el siguiente dato corresponde a la posición 1 delvector vec. Después de la primera iteración el estado de las variables será:

Otra forma de hacer tal incremento es empleando el operador ++ prefijo o postfijo: pvec++ o++pvec.Después de la segunda iteración:

y finalmente después de la vigésima:

Se recomienda volver ahora a la sección 9.3.4. y repasar el ejemplo y los operadores +, y - conapuntadores.

Note que después del ciclo el apuntador pvec, queda apuntando a una zona de memoria que nohace parte del vector vec.En general las operaciones sobre vectores con apuntadores suelen abreviar aún más losprogramas y eventualmente volverlos más eficientes, pero esto se paga con dificultad para leer yentender los programas.

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

pvec cont 0

vec : ? ? ? . . . ? ? 0 1 2 19

pvec cont 1

vec : 0 ? ? . . . ? ? 0 1 2 19

pvec cont 2

vec : 0 1 ? . . . ? ? 0 1 2 19

pvec cont 20

vec : 0 1 2 . . . 18 19 0 1 2 18 19

Page 13: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

Para completar, C internamente trata los vectores como apuntadores. Cuando declaramos unvector

int vi[10];

C entiende que vi será un apuntador a enteros, lo pone a apuntar a una zona de memoria conespacio para 10 enteros y no permite que el programador cambie la dirección a la que vi apunta.La primera ventaja radica en poder tratar la variable vi como un apuntador (excepto que nopodemos cambiar su valor), podemos asignar directamente el valor de vi a otro apuntador aentero:

int *pvi=vi;

Cuando se referencia alguna posición del vector vi, por ejemplo la posición 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 tamaño, como en lasiguiente caso:

int strlen(char s[]);

Esta declaración es equivalente a:

int strlen(char *s);

sólo que en el primer caso, al interior de la función 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:

intstrlen(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 condición del for (i.e *p!=‘\0’), simplementepor *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, elentero 0 equivale a falso y cualquier otra valor entero es verdadero)

Page 14: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

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 función anterior es importante destacar que retorna un apuntador a caracter, que emplea laconstante NULL para indicar que no encontró el caracter c dentro de la cadena s. Otracaracterística importante es que no requiere variables globales, emplea al parámetro s, pararecorrer la cadena. Esta función ilustra plenamente la simplificación que pueden tener lasfunciones cuando se emplean apuntadores, pero también muestra lo complicada que puede ser sulectura.

En general los parámetros de las funciones de las librerías string.h, son apuntadores.

Volvamos a la discusión iniciada al final de la sección anterior: Aritmética de apuntadores, paraello 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; /* También es valida la comparación de apuntadores */ /* cando ambos apuntan a elementos de un mismo vector */ /* Dara verdadero y lo asignará a vent[0] */ *ape2=ape1>ape2;

9.4.3. Memoria dinámica

La memoria es un importanterecurso del computador, esnecesario administrarla dealguna forma para asegurarque no se desperdicie omalgaste. Una de lasfunciones de un sistemaoperativo es administrar lamemoria.

Cuando se ejecuta unprograma, el sistemaoperativo le asigna variaszonas de memoria para que

SISTEMA OPERATIVO

“Un sistema operativo (SO) puede ser contemplado como unacolección organizada de extensiones software del hardware,consistente en rutinas de control que hacen funcionar uncomputador y proporcionan un entorno para la ejecución de losprogramas. Otros programas se apoyan en las facilidadesproporcionadas por el sistema operativo para obtener acceso alos recursos del sistema informático, tales como archivos ydispositivos de entrada/salida (E/S). Los programas invocangeneralmente los servicios del sistema operativo por medio dellamadas al sistema operativo. Además los usuarios puedeninteractuar con el sistema operativo directamente por medio deórdenes del sistema operativo. En cualquier caso, el sistemaoperativo actúa como interfaz entre los usuarios y el hardwarede un sistema informático.Internamente, un sistema operativo actúa como gestor de losrecursos del sistema informático, tales como el procesador, lamemoria, los archivos y los dispositivos de E/S.” [4]

Page 15: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

las emplee, se tratan de una zona para los datos del programa, una zona para el programa mismoy otra para el control de la ejecución y datos temporales.

Cada programa debe usar sólo la memoria que le ha sido asignada, de no ser así un programainquieto podría leer información de otras zonas de memoria o aún peor escribir en partes quepertenecen al sistema operativo o a otros programas.El sistema operativo DOS es especialmente frágil en la administración de memoria, a diferenciade otros sistemas como UNIX o Windows NT, donde el sistema vela para que ningún programa(excepto los designados en el kernel) escriba o lea de zonas de memoria que no le corresponden.

Una vez se termina la ejecución de un programa, el sistema operativo debe encargarse de“liberar” la memoria que había asignado para ese programa, y de esta forma hacerla disponiblepara otros programas.

Puede ocurrir que un programa requiera más memoria de la que el sistema operativo le asignó aliniciar la ejecución, esto puede hacerse desde C empleando varias funciones para administraciónde memoria. Algunas de estas funciones están declaradas en la librería stdio.h y otras en lalibrería alloc.h:

void *malloc(int size);void free(void *block);void *calloc(int nitems, int size);void *realloc(void *block, size_t size);

Por ahora nos dedicaremos a las funciones malloc y free. Con la función malloc, unprograma puede pedir una cantidad determinada de memoria, el compromiso que adquiereentonces es liberar esa memoria antes de terminar la ejecución con la función free.

ADVERTENCIA

Es muy importante liberar la memoria que se solicite, porque de lo contrario habrá memoriareservada sin emplear, memoria que otros programas o el sistema operativo podrían necesitar.Así mismo no debe emplearse memoria que no haya sido localizada o que haya sido liberada.

9.4.3.1. Apuntadores genéricos: Las funciones malloc y free trabajan con apuntadores avoid, estos son apuntadores genéricos, que pueden ser convertidos a otros tipos de apuntadorespor medio de casting. Veamos un ejemplo de como hacer casting entre apuntadores y de suspeligros:

En el contexto de este programa una asignación como: apfloat=apin; no es valida, porque apfloat es un apuntador a flotantemientras que apin es un apuntador a entero, cuando secompilará reportaría un error. Sin embargo la asignación apfloat=(apfloat *)apin

si es valida, hará que apfloat y apint apunten a la misma dirección de memoria Aún así esteprograma tiene un gravísimo problema cuando se ejecute. Haciendo un diagrama de memoria(suponiendo que estamos ejecutándolo en un sistema donde los tipos float e int tengandiferente tamaño) puede notarse el problema que genera la última instrucción.

int *apin;float *apfloat;int e;

apin=&e;apfloat=(float *)apin;*apfloat=10.5;

Page 16: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

9.4.3.2. malloc: Esta función recibe como parámetro el tamaño, en bytes, del bloque dememoria que el programa requiere. Si hay suficiente memoria para reservarlo, retorna unapuntador al bloque recién localizado. Si la memoria no alcanza retorna NULL.

Supongamos que estamos trabajando en un IBM PC, y queremos pedir espacio para almacenar undouble. La siguiente porción de programa realiza correctamente esta tarea (con excepción de queno 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 tamaño en bytes de cada tipo de datos quequeremos localizar C ofrece la primitiva sizeof, que recibe como parámetro un tipo o expresióny retorna el tamaño en bytes del tipo correspondiente. El ejemplo anterior empleando sizeofpuede 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 programaescrito en C es portable, cuando al pasar las fuentes de una máquina a otra, por compiladoresapropiados para cada máquina, se obtiene programas ejecutables que funcionan igual en ambasmáquinas. Todos los programas que hemos escrito hasta el momento son portables (excepto elprimer ejemplo de esta sección y la porción anterior que dice apd=(double *)malloc(8);),es decir que si los compilamos en una máquina IBM PC (por ejemplo con el compilador BC++3.1) o si los compilamos en un 486 (por ejemplo con el compilador gcc), obtendremosprogramas 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 algunade las funciones para localizar memoria: malloc, calloc y realloc. Libera tal bloque paraque pueda ser empleado por otros programas o por otras partes del mismo programa. Si reservaun bloque de memoria y luego lo libera con free, no debe volver a referenciar tal bloque dememoria (ya no está asignado y contendrá información de otra parte del programa)

Completemos el último ejemplo (aún falta verificar que hubiese memoria):

double *apd;apd=(double *)malloc(sizeof(double));*apd=0.00023222;free(apd);

Page 17: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

El programa mostrado a continuaciónes un ejemplo completo del uso dememoria dinámica.

Note que este programa verifica quemalloc asigne la memoria solicitada,en caso de que malloc falle en asignarlos 10 bytes solicitados, terminará yretornará 1.Si malloc logra localizar la memoria,copia en la zona asignada la cadena"Hola" terminada en '\0', es deciremplea 5 de los 10 bytes localizados yapuntados por cad.Además cuando termina de emplear lazona de memoria asignada la libera yretorna 0.

Es bastante común que los programasretornen 0 cuando no hay erroresdurante la ejecución y otros enteroscomo códigos de error.Ahora podemos hacer programas quepueden emplear diferentes cantidadesde memoria según sus necesidades entiempo de ejecución. Veamos elejemplo completo de la mediana de unconjunto de números:

1. Planteamiento del Problema y Teoría preliminar:

Dados n números reales, la mediana es el dato que quede en la posición n

2

después de ordenar

ascendentemente los n números.

Por ejemplo la mediana de los 5 números: 3,2,5,9,2 es 3.

3,2,5,9,2 Ordena 2,2,3,5,9 3er dato 3

2. Macro Algoritmoa) Pedir cantidad de datos y datos (se localiza memoria según la cantidad de datos)b) Ordenar los datos suministradosc) Extraer el dato de la mitad y mostrarlo como resultado.

3. Especificación de funciones

void Recibe(int n, float vec[]);/* PRE: Recibe la cantidad de datos (n) y un vector con espaciosuficiente 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 */

#include <string.h>#include <stdio.h>#include <alloc.h>

int main(){ char *cad; /* Localizamos memoria */ cad = (char *) malloc(10); if (cad==NULL) { printf("No hay Memoria\n"); return 1; }

/* copia "Hola" a la cadena */ strcpy(cad, "Hola");

/* muestra la cadena */ printf("cad es %s\n", cad);

/* libera memoria */ free(cad);

return 0;}

Page 18: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

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

4. Implementación

Del programa recién presentado se recomiendaobservar:• Asigna sólo la memoria necesaria para almacenar

los datos y la libera cuando ya no requiere más su contenido.• Verifica que malloc haya podido localizar la memoria solicitada. En caso de que no hubiese

podido muestra un mensaje y retorna un código de error.• Emplea la función inter_float que intercambia el valor de dos variables de tipo flotante.• Las funciones Ordena y Recibe reciben un vector de flotantes como parámetro, aunque

desde el main se está pasando un apuntador.• La invocación 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 unapuntador a un vector). Una aplicación típica es una lista de nombres, dado que cada nombre esuna cadena y las cadenas son vectores que pueden ser referenciados por medio de apuntadores.Veamos gráficamente un vector de apuntadores a vectores:

La declaración de vap es:

char *vap[4];

Como es un arreglo, podemos inicializarlodurante la declaración de la siguiente forma:char *vap[4]=

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

O aún más breve:

#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); }}

vap 'J' 'u' 'a' 'n' '\0' 0 1 'A' 'n' 'a' '\0' 2 3 'P' 'e' 'd' 'r' ''o' '\0'

'M' 'a' 'r' 'i' 'a' '\0'

Page 19: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

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 tendría que haberse dado desde el comienzo unacantidad de columnas fijo, igual al tamaño de la cadena más grande, además su inicializaciónsería más 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'}};

Además no podría 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++) { /*Imprime "Ana" */ printf("%c",mcar[y][x]);}

Ahora se desarrollará una aplicación completa que emplea un vector de apuntadores y ejemplificalos conceptos estudiados en todo el capítulo, se trata del programa SORT.

1. Planteamiento del problema

El programa SORT del DOS (y también de UNIX) recibe del usuario una lista de cadenas, ydespués se las muestra ordenadas de menor a mayor.

2. Macro Algoritmo

a) Pedir al usuario la cantidad de cadenas a ordenarb) Localizar memoria para el vector de cadenasc) Pedir al usuario las cadenas por ordenar y para cada una localizar memoria y ponerla en el

vectord) Ordenar las cadenas recibidase) Mostrar por pantallas las cadenas ordenadasf) Liberar toda la memoria solicitada

3. Especificación de funciones

void PideCadenas(int n,char *vc[]);/* PRE: Recibe la cantidad de cadenas a pedir y un vector paraapuntarlas *//* POST: Por cada cadena dada por el usuario se localiza memoriasuficiente y se apunta con un elemento del vector. Puede retornar 0 sino 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 apuntadoresa tales cadenas *//* POST: Reorganiza los apuntadores del vector vc para que las cadenasa las que apuntan quede ordenadas alfabeticamente */

void MuestraCadenas(int n,char *vc[]);

Page 20: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

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

5. Implementación La función 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 unalínea entera del usuario y la almacene en una cadena, empleamos la función gets definida enstdio.h, su especificación es la siguiente:

char *gets(char *s);/* PRE: Recibe una cadena s, con suficiente espacio para la linea queingresará el usuario *//* POST: Llena el vector s, con los caractéres de la linea dada por elusuario (terminada con '\n') y retorna un apuntador a tal linea (s). Siencuentra un fin de archivo (EOF) retorna NULL*/

Además no es conveniente mezclar la función scanf con gets, pues puede generarcomportamientos a primera vista extraños. Por tal motivo el primer entero solicitado se pidecomo una línea y después se convierte a entero empleando la función atoi, declarada en lalibrería stdlib.h y cuya declaración y especificación se presentan a continuación:

int atoi(const char *s); /* PRE: Recibe una cadena s *//* POST: Convierte la cadena s a entero y lo retorna. Asume que lacadena s se compone de: 1. Una cadena opcional de espacios otabuladores, 2. Un signo opcional (+/-), 3. Una cadena de digitos. Sino puede completar la conversión retorna 0*/

La implementación se muestra en las páginas siguientes.

6. Inconvenientes

El programa mostrado, compara cadenas teniendo en cuenta capitalización (mayúsculas yminúsculas). e.g. La cadena "Zimbawe" es menor que "altura".

En realidad el programa SORT, no pregunta al usuario la cantidad de líneas a ordenar, sino queempieza a leer líneas 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 máquina UNIXpresionando [Control]-[D])

Usa la función fgets que no es segura.

Emplea un algoritmo de ordanamiento poco eficiente.

Page 21: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

#include <stdlib.h>#include <stdio.h>#include <alloc.h>#include <string.h>

#define MAX_TAM_LINEA 100 /* Tamaño máximo que puede tener una linea */

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[]);

intmain() {

int n; /* Número 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("Número 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 tamaño de un char * ??? */ if (Lineas==NULL) { printf("Memoria insuficiente\n"); return 1; }

if (PideCadenas(n,Lineas)) { /* ??? Porque la condición de este if no tiene operadores de comparación ???*/ 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); /* Liberamos también el vector de apuntadores */ return 0;}

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 posición dentro del vector que apunta a la mayor cadena encontrada */

char *max; /* Apuntador a la cadena más grande encontrada */ int pmax; /* Posición dentro del vecto a la cadena más 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 ocurriría si dijera strcmp(max,cad[i]) ??? */ max=cad[i]; pmax=i; } }

return pmax;}

Page 22: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

voidMuestraCadenas(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 quedeordenadas 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 vectorrecibido */

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;}

Page 23: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

Argumentos de un programa

Hasta el momento se ha trabajado con una función main que no recibe argumentos y retorna unentero:int main()

En realidad la función main, si puede tener parámetros, corresponderán a los argumentos pasadosen la línea 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 elnombre del archivo fuente y el segundo el nombre del archivo destino.

Cuando se invoca un programa escrito en C, estos argumentos quedarán organizados en un vectorde cadenas, este vector siempre se identifica con el parámetro argv , la cantidad de parámetrosrecibidos queda en una variable de tipo entera que puede recibir el main como argc. Ladefinición del main, cuando se van a recibir los parámetros deberá comenzar así:

intmain(int argc,char *argv[])

Cada cadena de argv, será un parámetro de la línea de comandos, el primero es el nombre delprograma (por está razón argc, siempre es mínimo 1) por ejemplo el comando

echo Esta es una prueba

tanto en DOS como en UNIX imprimirá elmensaje "Esta es una prueba". Si echo fuese unprograma escrito en C, los parámetros argc y argvserán como se muestran en la figura.

Una vez dentro del main, podrán usarse losargumentos recibidos, como se ejemplifica a en elsiguiente programa que imprime todos losargumentos que recibe:

#include <stdio.h>intmain(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 función scanf (librería stdio.h) recibe como parámetros apuntadores? Queocurriría si en vez de esto recibiera valores?

2. Que valor adquiere la variable i, una vez se ejecuta la siguiente porción de programa?

argc argv 5 "Echo"

"Esta"

"es"

"una"

"prueba"

Page 24: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

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 porción de código, 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 función strchr mostrada en uno de los ejemplos de la sección 9.4.3, puede ser aún másbreve, aprovechando que el ASCII de ‘\0’ es 0. Escriba nuevamente esta funciónaprovechando esto. (Ayuda: Puede aplicarse 2 veces)

5. Cual es el estado de las variables cuando se termina de ejecutar la porción de código delúltimo ejemplo de la sección 9.4.2 ?

6. Haga diagramas de memoria para la porción de programa del primer ejemplo de la sección9.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 lasección 9.4.3.3

9. Porque cree que a una compañía de software le interesan lenguajes de programaciónportables?

10.Declare un apuntador a flotantes, y empléelo para apuntar a un vector de 10 flotanteslocalizado dinámicamente 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 sección 9.4.3, la función Ordena, hace la invocación:inter_float(vec+i,vec+i+1), que ocurre si cambiamos esa invocación por:inter_float(vec[i],vec[i+1]) ?

12.Defina un vector de apuntadores a vectores de caracteres (o más breve un vector de cadenas),con las siguientes cadenas (en este orden): "El", "Lenguaje", "de", "Programación", "C".También defina una matriz de caracteres con las mismas cadenas.

13.Que ventajas tiene un vector de cadenas (Ejemplo 1 sección 9.4.4) sobre una matriz decaracteres? Piense por ejemplo en espacio requerido por cada uno y facilidad de uso.

Page 25: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

14.Haga la función atoi cuya especificación se presenta en el ejemplo SORT de la sección 9.4.4.

15.En el código fuente del programa SORT se presentan varias líneas de comentarios,comenzadas con /*???. Son preguntas referentes al programa, contéstelas todas.

9.6. EJERCICIOS

Sección 9.4.1.

1. Se requiere una función que reciba 3 enteros y retorne los 3 enteros ordenados de menor amayor.

2. Haga la función Halla_Max, que recibe un vector de enteros y el tamaño de tal vector.Retorna la posición del máximo y el máximo elemento del vector. (Ayuda: Puede retornar unoo ambos valores en parámetros pasados por referencia)

Sección 9.4.2.

1. Haga la versión con apuntadores de la función strcat de la librería string.h. La siguienteespecificación fue tomada del Help del BC ++ 3.1

Agrega una cadena al final de otraDeclaración:char *strcat(char *dest, const char *src);

Comentarios:strcat concatena una copia de src al final de dest. La longitud de la cadena resultante esstrlen(dest) + strlen(src). Valor retornado:strcat retorna un apuntador a las cadenas concatenadas (es decir a src)

2. Haga la versión con apuntadores de la función strchr de la librería string.h. La siguienteespecificación fue tomada del Help del BC ++ 3.1. Mire con cuidado los comentarios y notaráque la función presentada en este capítulo no cumple todos los requerimientos.

Busca en una cadena la primera ocurrencia de un caracter dadoDeclaración:char *strchr(char *s, int c);

Comentarios:strchr hace un recorrido hacia adelante, buscando el caracter especificado. Encuentra laprimera 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 versión con apuntadores de la función strcmp de la librería string.h. La siguienteespecificación fue tomada del Help del BC ++ 3.1.

Compara dos cadenasDeclaración:int strcmp(char *s1,char*s2);

Page 26: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

Comentarios:strcmp hace una comparación sin signo de las cadenas s1 y s2.La comparación de las cadenas comienza con el primer carácter de cada cadena y continua conlos siguientes hasta que los caracteres correspondientes difieran o hasta que termine se alcance elfinal de la cadena.Valor retornado:Esta rutina retorna un valor de tipo int, que cumple:< 0 si s1<s2si s1>s2== 0 si s1==s2

4. Haga la versión con apuntadores de la función strcpy de la librería string.h. La siguienteespecificación fue tomada del Help del BC ++ 3.1.

Copia una cadena fuente en una destinoDeclaración:char *strcpy(char *dest, char *src);

Comentarios:Copia la cadena src en dest, deteniéndose después de haber copiado el caracter de terminaciónde cadena.Valor retornado:Apuntador a la cadena destino dest

5. Haga la versión con apuntadores de la función strlen de la librería string.h. La siguienteespecificación fue adaptada del Help del BC ++ 3.1.

Calcula la longitud de una cadenaDeclaración:int *strlen(char *s);

Comentarios:Calcula la longitud de la cadena sValor retornado:Retorna el número de caracteres de la cadena s, sin contar el caracter de fin de cadena.

6. Haga la versión con apuntadores de la función strlwr de la librería string.h. La siguienteespecificación fue tomada del Help del BC ++ 3.1.

Convierte una cadena a minúsculasDeclaración:char *strlwr(char *s);

Comentarios:Convierte las letras mayúsculas (A - Z) de la cadena s a letras minúsculas (a - z).Valor retornado:

Un apuntador a la cadena s

Sección 9.4.4.

1. Haga la función Ordena del programa ejemplo SORT, pero empleando el algoritmo deordenación por burbuja.

2. Haga los cambios necesarios al programa SORT para que no distinga mayusculas deminusculas.

Page 27: 9. APUNTADORES EN C - unal.edu.colctorress/estructuras/pdf/EstA... · 2019. 7. 7. · complejas que vectores o matrices, como es el caso de listas, arboles y grafos. Otra aplicación

3. Modifique el programa SORT, de forma que no pida la cantidad de cadenas al comienzo, sinoque pida lineas al usuario hasta que él envie un fin de archivo EOF. (Ayuda: La función getsretorna 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 "Parámetro invalido" en otro caso.

9.7. PROYECTOS

1. Existen programas que permiten obtener estadísticas de datos, (SPSS es uno de los másfamosos). Inclusive las calculadoras sencillas comúnmente traen algunas funciones deestadística. Haga un programa que permita:1. Editar los datos por analizar (el número de datos debe ser variable)2. Calcular promedio 3. Calcular mediana4. Calcular desviación estándar5. Calcular error cuadrático medioComo restricción, debe mantener los datos en un vector que se localice dinámicamente.

2. Haga un rograma que cuente la frecuencia de las palabras provenientes de la entrada estándar.Por ejemplo si el usuario tecleara:Solo se que nada sewc retornaría una lista comoSolo 1se 2que 1nada 1Haga este programa, de forma que espere líneas provenientes de la entrada estándar hasta quellegue un caracter fin de archivo, y muestre por la salida estándar una lista de las palabras deltexto de entrada, junto con su frecuencia, sin tener en cuenta capitalización ni signos depuntuación. Debe emplear un vector de apuntadores para mantener las palabras digitadas por elusuario y otro para mantener la frecuencia. Opcionalmente la salida puede estar ordenadaalfabéticamente.

BIBLIOGRAFÍA[1] BORLAND C++ 5.0. Programmer´s Guide. 1996

[2] KERNIGHAN, Brian. RITCHIE, Dennis M. El lenguaje de programación C. EditorialPrentice Hall. 1985

[3] DEITEL, H. M. DEITEL, P.J. C How to Program. Editorial Prentice Hall. 1992.

[4] MILENKOVIC, Milan.Sistemas Operativos: Conceptos y diseño. Mc Graw Hill. 1994.

CREDITOS, PROPIEDAD INTELECTUAL: Dominio público. 2004. Sin garantí[email protected]