25
1 Capítulo 1. Apuntadores 1.1 Conceptos básicos. Para entender qué es un puntero veremos primero cómo se almacenan los datos en un computador. La memoria de un computador está compuesta por unidades básicas llamadas bits. Cada bit sólo puede tomar dos valores, normalmente denominados alto y bajo, ó 1 y 0. Pero trabajar con bits no es práctico, y por eso se agrupan. Cada grupo de 8 bits forma un byte u octeto. En realidad el microprocesador, y por lo tanto nuestro programa, sólo puede manejar directamente bytes o grupos de dos o cuatro bytes. Para acceder a los bits hay que acceder antes a los bytes. Cada byte tiene una dirección, llamada normalmente dirección de memoria. 1.1.1 Definición de Apuntadores Un apuntador es una variable, que almacena como contenido una dirección de memoria, de otra variable a la que apunta, dicha dirección representa el lugar donde se almacena un dato. Los apuntadores tienen un tipo de dato específico y solo pueden apuntar a espacios de memoria con datos del mismo tipo. Por supuesto, a partir de esa dirección de memoria puede haber cualquier tipo de objeto: un char, un int, un float, un array, una estructura, una función u otro puntero. Seremos nosotros los responsables de decidir ese contenido. Con los apuntadores es posible manipular estructuras de datos o asignar memoria dinámica. Los apuntadores son una de las herramientas más poderosas con que cuenta el Lenguaje C++. Desafortunadamente, muchos programadores han creado el mito de que el estudio de los apuntadores es muy complicado, lo cual ha desarrollado una fobia entre quienes se inician en el estudio de las estructuras dinámicas en lenguaje C/C++. 1.1.2 Declaración de apuntadores Los apuntadores son variables automáticas cuyos valores representan direcciones de memoria correspondientes a otras variables. La sintaxis para la declaración de un apuntador es la siguiente: tipo * identificador ; Ejemplo: int *apunt; // Declaración del apuntador apunt // Se dice que: "apunt va a apuntar a variables de tipo int" donde: apunt es el nombre del apuntador y (*) es el operador de indirección En el ejemplo anterior, puede decirse que: *apunt se refiere al objeto apuntado por apunt. apunt es un apuntador a objetos de tipo int Observe que el operador de indirección utiliza el mismo símbolo que el operador de multiplicación. En este caso el asterisco le indica al sistema que se define una variable apuntador. Ejemplos: int *x; a es un apuntador de tipo entero. char *y; c es un apuntador de tipo carácter. double *p, *q; p y q son apuntadores de tipo real doble precisión. 1.1.3 Operadores para trabajar apuntadores Los operadores utilizados para trabajar variables apuntadores son el ( * ) asterisco llamado operador de indirección, y el ( & ) ampersand, llamado operador de dirección.

Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

  • Upload
    lylien

  • View
    223

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

1

Capítulo 1. Apuntadores 1.1 Conceptos básicos. Para entender qué es un puntero veremos primero cómo se almacenan los datos en un computador. La memoria de un computador está compuesta por unidades básicas llamadas bits. Cada bit sólo puede tomar dos valores, normalmente denominados alto y bajo, ó 1 y 0. Pero trabajar con bits no es práctico, y por eso se agrupan. Cada grupo de 8 bits forma un byte u octeto. En realidad el microprocesador, y por lo tanto nuestro programa, sólo puede manejar directamente bytes o grupos de dos o cuatro bytes. Para acceder a los bits hay que acceder antes a los bytes. Cada byte tiene una dirección, llamada normalmente dirección de memoria. 1.1.1 Definición de Apuntadores Un apuntador es una variable, que almacena como contenido una dirección de memoria, de otra variable a la que apunta, dicha dirección representa el lugar donde se almacena un dato. Los apuntadores tienen un tipo de dato específico y solo pueden apuntar a espacios de memoria con datos del mismo tipo. Por supuesto, a partir de esa dirección de memoria puede haber cualquier tipo de objeto: un char, un int, un float, un array, una estructura, una función u otro puntero. Seremos nosotros los responsables de decidir ese contenido. Con los apuntadores es posible manipular estructuras de datos o asignar memoria dinámica. Los apuntadores son una de las herramientas más poderosas con que cuenta el Lenguaje C++. Desafortunadamente, muchos programadores han creado el mito de que el estudio de los apuntadores es muy complicado, lo cual ha desarrollado una fobia entre quienes se inician en el estudio de las estructuras dinámicas en lenguaje C/C++. 1.1.2 Declaración de apuntadores Los apuntadores son variables automáticas cuyos valores representan direcciones de memoria correspondientes a otras variables. La sintaxis para la declaración de un apuntador es la siguiente: tipo * identificador ; Ejemplo: int *apunt; // Declaración del apuntador apunt // Se dice que: "apunt va a apuntar a variables de tipo int" donde: apunt es el nombre del apuntador y (*) es el operador de indirección En el ejemplo anterior, puede decirse que: *apunt se refiere al objeto apuntado por apunt. apunt es un apuntador a objetos de tipo int Observe que el operador de indirección utiliza el mismo símbolo que el operador de multiplicación. En este caso el asterisco le indica al sistema que se define una variable apuntador. Ejemplos: int *x; a es un apuntador de tipo entero. char *y; c es un apuntador de tipo carácter. double *p, *q; p y q son apuntadores de tipo real doble precisión. 1.1.3 Operadores para trabajar apuntadores Los operadores utilizados para trabajar variables apuntadores son el ( * ) asterisco llamado operador de indirección, y el ( & ) ampersand, llamado operador de dirección.

Page 2: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

2

• * toma su operando como una dirección de memoria y retorna la información almacenada en ese lugar.

• & devuelve la dirección de memoria de su operando. Veamos un ejemplo llamado Programa1.cpp en el siguiente listado. 1.1.4 Aplicación práctica del uso de apuntadores Progra1.cpp #include <iostream.h> #include <conio.h> void main(void) { int x, y; Define x, y variables enteras. int *p, *q; Define p y q variables tipo apuntador. p = &x; asigna a p la dirección de x. (p apunta a x). q = &y; asigna a q la dirección de y. (q apunta a y). *p = 10; almacena en x el valor 10. *q = *p * 2; almacena en y el valor de x multiplicado por 2 (20). y = y + *p; a la variable y le suma el valor en x (20+10). cout<<*p; imprime el contenido de x (10). cout<<,*q; imprime el contenido de y (30). getch(); } 1.2 Variables automáticas y apuntadores Las variables automáticas se crean en tiempo de compilación y se destruyen al terminar la ejecución del módulo donde fueron declaradas. Aunque no es estrictamente necesario, se pueden manejar las variables automáticas por medio de apuntadores, veamos un ejemplo. 1.2.1 Implementación de variables automáticas Progra2.cpp #include <iostream.h> #include <conio.h> void main() { int automatica ; // Se declara la variable automatica. int *apunt ; // Se declara el apuntador apunt, que apuntará a objetos de tipo int. automatica = 100 ; // Se asigna el valor 100 a la variable automática. apunt = &automatica ; // Se asigna a apunt la dirección de automatica ó apunt apunta a // automatica. clrscr(); cout << "VALOR=" << automatica << " \n"; // 100 *apunt="200" ; // Se asigna el valor 200 al objeto apuntado- // por apunt. cout << "VALOR=" << automatica << " \n"; // 200 getch(); }

Page 3: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

3

1.2.2 Análisis de Progra2.cpp Las instrucciones del listado anterior se traducen en la siguiente secuencia, donde los apuntadores se representan con una flecha (para simular que "apuntan hacia" ó "señalan" un objeto) y los objetos apuntados se representan por un cuadro (para simular un recipiente). INSTRUCCION REPRESENTACION GRAFICA int automatica ; automatica int *apunt ; ----> ? automatica = 100 ; automatica apunt = &automatica ; automatica, *apunt apunt ----> *apunt = 200 ; automatica, *apunt Apunt ----> Un apuntador es una variable que solo puede contener un valor a la vez, por lo que solo puede apuntar a un objeto al mismo tiempo. Por otro lado, una variable cualquiera puede ser apuntada (referenciada) por varios apuntadores, ya que su dirección de memoria puede ser almacenada en distintas variables a la vez. Al declarar un apuntador, se está especificando el tipo de variable al que va a apuntar. Por ejemplo, no podrá declararse un apuntador a objetos de tipo int y después intentar utilizarlo para apuntar a objetos de tipo float. Cuando se desee manejar un apuntador a cualquier tipo de objeto, se puede declarar de tipo void, como en: void *multiusos; En el siguiente listado se muestra un ejemplo de aplicación de un apuntador de tipo void. 1.2.3 Aplicación de apuntador tipo void Progra3.cpp #include <iostream.h> #include <conio.h> #define NL cout << "\n"; void main() { int varent="0" ; float varflot="0.0" ; void *apmulti="&varent”; // apmulti APUNTA A varent *(int *)apmulti="2" ; // ASIGNA 2 AL OBJETO NL; // APUNTADO POR apmulti cout << varent ;

?

100

100

200

Page 4: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

4

apmulti="&varflot" ; // apmulti APUNTA A varflot *(float *)apmulti="1.1" ; // ASIGNA 1.1 AL OBJETO APUNTADO // POR apvoid NL; cout << varflot ; NL; getch(); } 1.2.4 Análisis de Progra3.cpp Analicemos la siguiente instrucción: *(int *)apmulti = 2 ; en donde: apmulti es un apuntador de tipo void. (int *)apmulti está forzando a que apmulti apunte a objetos de tipo int. *(int *)apmulti se refiere a un objeto de tipo entero apuntado por apmulti. 1.3 Apuntadores y cadenas Una cadena es un arreglo de caracteres cuyo último elemento es el caracter nulo. Utilizando la nomenclatura de arreglos, podemos declarar: char nombre[ ] = "COMERCIO" ; Esto mismo puede hacerse por medio de apuntadores, como se muestra en siguiente ejemplo. 1.3.1 Implementación de apuntadores y cadenas Progra4.cpp #include <iostream.h> #include <conio.h> #include <stdio.h> void main() { char *nombre = "COMERCIO" ; clrscr(); gotoxy(30,12); cout<< "!! HOLA, " ; puts(nombre); gotoxy(43,12); cout << " !!"; getch(); } 1.3.2 Arreglos de apuntadores Los apuntadores pueden manejarse en un arreglo, de tal forma que: char nombres[ ][5] = { "HUGO", "PACO", "LUIS" } ; es la declaración de un arreglo de cadenas, con asignación de valores iniciales. Su equivalente en notación de apuntadores es: char *nombres[ ] = { "HUGO", "PACO", "LUIS" } ; en el que se declara un arreglo de apuntadores. El programa completo para el manejo de este ejemplo.

Page 5: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

5

1.3.3 Implementación de arreglos de apuntadores Progra5.cpp #include <iostream.h> #include <conio.h> #include <string.h> void main() { char *nombres[ ] = { "HUGO", "PACO", "LUIS" } ; char invitado[11]; int bandera; clrscr(); gotoxy(30,10); cout << "CUAL ES SU NOMBRE ? " ; gotoxy(50,10); cin>> invitado ; gotoxy(30,12); for( int x = 0 ; x <3 ; x++ ) if(strcmp(invitado, nombres[x])="=" 0) bandera="0;" if(bandera="=" 0) cout << "!! PASE, ESTIMADO " << invitado << " !!"; else cout << "!! FUERA DE AQUI, " << invitado << " !!"; getch(); } 1.4 Paso de arreglos como parámetros Un arreglo puede pasarse como parámetro a una función. Si tuviera que pasarse por valor un arreglo muy grande, sería un desperdicio de memoria. En el Lenguaje C++ el paso de arreglos se hace por referencia, ya que el nombre del arreglo corresponde a un apuntador al primer elemento del arreglo. Al pasar un parámetro correspondiente a un arreglo, se pasa la dirección del primer elemento, por lo que la función invocada puede modificar cualquier elemento del arreglo. El siguiente programa maneja una función llamada nputs(), la cual recibe como parámetro un arreglo de caracteres. 1.4.1 Implementación de paso de arreglos como parámetros Progra6.cpp #include <iostream.h> #include <conio.h> #include <stdio.h> #include <string.h> void nputs(char *); void main() { char cadena[81]; clrscr(); gotoxy(10,10); cout << "ESCRIBA UNA CADENA: "; gets(cadena); gotoxy(10,12); nputs(cadena); getch(); } void nputs(char cad[ ]) { int x="0;" while(cad[x]) { cout << cad[x] ; x++; } }

Page 6: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

6

1.4.2 Implementación de la función nputs() por medio de apuntadores En el siguiente listado se muestra el manejo de la función nputs(), por medio de apuntadores. Progra7.cpp #include <iostream.h> #include <conio.h> #include <stdio.h> #include <string.h> void nputs(char *); void main() { char cadena[81]; clrscr(); gotoxy(10,10); cout << "ESCRIBA UNA CADENA: "; gets(cadena); gotoxy(10,12); nputs(cadena); getch(); } void nputs(char *cad) { while(*cad) cout << *cad++ ; } 1.4.3 Paso de funciones como parámetros Toda función tiene asociada una dirección de inicio de código, la cual puede pasarse un apuntador como parámetro en la invocación a otra función. Veamos un ejemplo de paso de punteros como parámetros a funciones. 1.4.4 Implementación del paso de funciones como parámetros Progra8.cpp #include <iostream.h> #include <string.h> #include <conio.h> int cmpcad(char*, char*); void compara(char*, char*, int(*)(char*, char*)); void main() { char cadx[80], cady[80]; clrscr(); gotoxy(10,5); cout << "ESCRIBA UNA CADENA : " ; cin>> cadx; gotoxy(10,7); cout << "ESCRIBA OTRA CADENA : " ; cin>> cady; gotoxy(10,9); compara(cadx, cady, cmpcad); gotoxy(1,24);

Page 7: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

7

} void compara(char *cad1, char *cad2, int (*cmpcad)(char*, char*)) { if(!(*cmpcad)(cad1,cad2)) cout << "LAS CADENAS SON IGUALES"; else cout << "LAS CADENAS SON DISTINTAS"; } int cmpcad(char *x, char *y) { return(strcmp(x,y)); } Expliquemos la expresión que puede ser un tanto desconocida del listado anterior, la expresión: int(*cmpcad)(char*, char*) establece que cmpcad es un apuntador a una función, la cual devuelve un valor de tipo entero. 1.5 Apuntadores a apuntadores Como se vio al principio de la unidad, un apuntador también es una variable. Su dirección puede ser almacenada por otra variable apuntador, por lo que puede hablarse de un apuntador a un apuntador. Esto puede extrapolarse para dos o más variables, como se observa en el ejemplo siguiente de apuntadores a apuntadores. 1.5.1 Implementación de apuntadores a apuntadores Progra9.cpp #include <iostream.h> #include <conio.h> void main() { int x, *a, **b, ***c ; // 1 clrscr(); a = &x ; // 2 *a = 100 ; // 3 b = &a ; // 4 **b += *a ; // 5 c = &b ; // 6 ***c += **b + *a ; // 7 cout << " *a=" << *a << " \n" ; cout << " **b=" << **b << " \n" ; cout << "***c=" << ***c << " \n" ; getch(); } 1.5.2 Análisis de Progra9.cpp Explicando las líneas marcadas en el listado anterior se tiene. int x, *a, **b, ***c; // 1

Page 8: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

Se declaran: x como una variable de tipo entero. a a un apuntador, el cual a su vez apuntará a objetos de tipo entero. Se dice que b es "el apuntador del apuntador". c como un apuntador a un apuntador que apunta a otro apuntador, el cual a su vez apunta a objetos de tipo entero. Se dice que c es "el apuntador del apuntador del apuntador". La pila luciría así:

a = &x ; // 2 Se asigna, al apuntador a, la dirección de x. La pila luciría así:

*a = 100 ; // 3 Al objeto apuntado por a se le asigna

b = &a ; // 4 Al apuntador b se le asigna la dirección del apuntador a. La pila luciría así:

**b += *a ; // 5 Al objeto apuntado por el apuntador apuntado por b se le suma el valor del objeto apuntado por a. La pila luciría así:

a como un apuntador a objetos de tipo entero. a un apuntador, el cual a su vez apuntará a objetos de tipo entero. Se dice que b es "el apuntador del

a un apuntador que apunta a otro apuntador, el cual a su vez apunta a objetos de tipo entero. Se dice que c es "el apuntador del apuntador del apuntador".

Se asigna, al apuntador a, la dirección de x. La pila luciría así:

Al objeto apuntado por a se le asigna el valor 100. La pila luciría así:

Al apuntador b se le asigna la dirección del apuntador a. La pila luciría así:

Al objeto apuntado por el apuntador apuntado por b se le suma el valor del objeto apuntado por a. La

8

como un apuntador a objetos de tipo entero. b como un apuntador a un apuntador, el cual a su vez apuntará a objetos de tipo entero. Se dice que b es "el apuntador del

a un apuntador que apunta a otro apuntador, el cual a su vez apunta a objetos de tipo entero. Se dice que c es "el apuntador del apuntador del apuntador".

Al objeto apuntado por el apuntador apuntado por b se le suma el valor del objeto apuntado por a. La

Page 9: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

c = &b ; // 6 Al apuntador c se le asigna la dirección del apuntador b. La pila luciría así:

***c += **b + *a ; // 7 Se asigna al objeto apuntado por el apuntador apuntado por el apuntador c, el valor del objeto apuntado por el apuntador apuntado por el apuntador b más el valor del objeto apuntado por el apuntador a. La pila luciría así:

1.5.3 Apuntadores y arreglos El tema de los arreglos está íntimamente ligado al de apuntadores; tanto que es posible intercambiarlos en la solución de un problema. El nombre de un arreglo corresponde al de un apuntador que almacena un valor constante. Este valor constante es la dirección de memoria del primer elemento del arreglo. Por ejemplo : int calif[ ]={100,90,95,80,90}; // Declaración e inicialización // enterosSe puede representar con la figura siguiente.

Al apuntador c se le asigna la dirección del apuntador b. La pila luciría así:

Se asigna al objeto apuntado por el apuntador apuntado por el apuntador c, el valor del objeto apuntado por el apuntador apuntado por el apuntador b más el valor del objeto apuntado por el apuntador a. La

El tema de los arreglos está íntimamente ligado al de apuntadores; tanto que es posible intercambiarlos

bre de un arreglo corresponde al de un apuntador que almacena un valor constante. Este valor constante es la dirección de memoria del primer elemento del arreglo.

]={100,90,95,80,90}; // Declaración e inicialización de un arreglo de 5 enteros.

siguiente.

9

Se asigna al objeto apuntado por el apuntador apuntado por el apuntador c, el valor del objeto apuntado por el apuntador apuntado por el apuntador b más el valor del objeto apuntado por el apuntador a. La

El tema de los arreglos está íntimamente ligado al de apuntadores; tanto que es posible intercambiarlos

bre de un arreglo corresponde al de un apuntador que almacena un valor constante. Este valor

Page 10: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

10

En realidad, el lenguaje manejará al arreglo a través de un apuntador llamado calif, el cual tiene almacenado el valor 65494, que a su vez corresponde a la dirección de inicio del elemento calif[0]. La representación del apuntador calif, en la zona de variables globales de la memoria RAM, es la siguiente :

Calif 65494

En el siguiente listado se presenta el manejo del arreglo calif[ ], a través de la notación de arreglos, y en el listado subsiguiente llamado Progra11.cpp el manejo con la notación de apuntadores. Manejo de calif[ ] , notación de arreglos Progra10.cpp #include <iostream.h> void main() { int calif[ ] = { 100,90,95,80,90}; for(int i=0 ; i <5 ; i++) //Notación de arreglos. cout << "\n" << calif[i] ; } 1.5.4 Notación de apuntadores De igual manera que la notación arreglos se presenta el manejo de calif[ ] en notación de apuntadores. Progra11.cpp #include <iostream.h> void main() { int calif[ ] = { 100,90,95,80,90}; for(int i=0 ; i <5 ; i++) // Notación de apuntadores cout << "\n" << *(calif+i) ; }

Page 11: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

1.5.5 Análisis de Progra11.cpp Como puede observarse, la única diferencia entre los listados primero utiliza calif[i] y el segundo *(calif+i). Debido a que la ejecución de los programas de ambos listados producen resultados iguales, se deduce que: calif[i] == *(calif+i) Para entender esto que a simple vista no es obvio, revisaremos algunos conceptos: 1. El nombre del arreglo corresponde al de un apuntador que apunta al primer elemento del arreglo, por lo que: calif apunta a calif[0] Visto gráficamente:

2. Para hacer referencia a un elemento específico del arreglo, se toma como base la dirección del primer elemento y, con el subíndice del elemento referirse al segundo elemento del arreglo puede procederse así calif[1] // Notación de arreglos ó *(calif+1) // Notación de apuntadores, donde la expresión calif+1sirve para calcular la dirección elemento que está una posición más allá del elemento apuntado por calif. Para referirse a calif[2] ( tercer elemento ), puede escribirse:*(calif+2) Lo que significa: "El objeto que se encuentra dos posiciones después del objeto apuntado por calif". este caso, una posición es el espacio requerido por cada uno de los elementos, de tal manera que, si calif apunta a la dirección 65494, entonces calif+2 es la expresión que calcula la dirección 65498. La figura muestra los elementos del arreglo calif[de apuntadores.

De lo anterior, se infiere la regla: calif[i] == *(calif+i)

observarse, la única diferencia entre los listados llamados Progra10primero utiliza calif[i] y el segundo *(calif+i).

Debido a que la ejecución de los programas de ambos listados producen resultados iguales, se deduce

Para entender esto que a simple vista no es obvio, revisaremos algunos conceptos: 1. El nombre del arreglo corresponde al de un apuntador que apunta al primer elemento del arreglo, por

Para hacer referencia a un elemento específico del arreglo, se toma como base la dirección del primer elemento y, con el subíndice del elemento específico, se calcula su dirección. Por ejemplo, para

el arreglo puede procederse así:

// Notación de apuntadores, donde la expresión calif+1sirve para calcular la dirección elemento que está una posición más allá del elemento apuntado por calif. Para referirse a calif[2] ( tercer elemento ), puede escribirse:

Lo que significa: "El objeto que se encuentra dos posiciones después del objeto apuntado por calif". este caso, una posición es el espacio requerido por cada uno de los elementos, de tal manera que, si calif apunta a la dirección 65494, entonces calif+2 es la expresión que calcula la dirección 65498. La figura muestra los elementos del arreglo calif[ ] con sus nombres en notación de arreglos y en notación

11

llamados Progra10 y 11 es que el

Debido a que la ejecución de los programas de ambos listados producen resultados iguales, se deduce

Para entender esto que a simple vista no es obvio, revisaremos algunos conceptos: 1. El nombre del arreglo corresponde al de un apuntador que apunta al primer elemento del arreglo, por

Para hacer referencia a un elemento específico del arreglo, se toma como base la dirección del específico, se calcula su dirección. Por ejemplo, para

// Notación de apuntadores, donde la expresión calif+1sirve para calcular la dirección del

Lo que significa: "El objeto que se encuentra dos posiciones después del objeto apuntado por calif". En este caso, una posición es el espacio requerido por cada uno de los elementos, de tal manera que, si calif apunta a la dirección 65494, entonces calif+2 es la expresión que calcula la dirección 65498. La

con sus nombres en notación de arreglos y en notación

Page 12: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

12

por lo que: &calif[i] == calif+i Esto es que, la dirección del i-ésimo elemento de un arreglo se calcula sumándole el subíndice a la dirección del primer elemento. Como otro ejemplo, supongamos la siguiente declaración correspondiente a un arreglo de 10 elementos de tipo float. float* sueldo[10]; Si la dirección del primer elemento es 65494, entonces: &sueldo[5] es igual a : sueldo+5 = 65494 + ( 5 * 4 ) = 65514 Como regla, podemos establecer que el subíndice se refiere a la posición del elemento en el arreglo. Es por esto que al calcular la dirección por medio del subíndice, éste debe multiplicarse por el número de bytes que representan el tamaño de cada elemento (dado por el tipo utilizado en la declaración del arreglo). 1.5.6 Operaciones con apuntadores

• Se pueden realizar asignaciones entre punteros. int a = 15; int *p, *q; q = &a; p = q; /* se asigna la dirección que contiene q a p */ cout<<p; /* imprime la dirección almacenad en p. */

• Se pueden operar solamente el +, el -, el ++ y el --. Int p; p = p + 1; p avanza un entero. p = p – 2; p retrocede dos enteros. p++; p apunta al siguiente entero. p--; p apunta al entero anterior.

• Los punteros se pueden comparar. int a; int *b, *c; if (b + n > c) b = b +2;

En el siguiente código se realiza un programa que emplea los operadores (& y *). Progra12.cpp #include <iostream.h> #include <conio.h> int main() { clrscr(); int a; //a es un puntero

Page 13: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

13

int * ap; //ap es un apuntador a un puntero a = 7; ap= & a; //ap toma la dirección de a cout <<"la dirección de a es " <<&a; cout<<" \n el Valor de ap es " << ap; cout <<"\n el valor de a es" <<a; cout<<"\n el valor de *ap es " << *ap; cout<<"\n\n\n Mostrando los valores de * y &" ; cout <<"\n &* ap = " <<&*ap; cout <<"\n *& ap = " << *&ap; getch(); return 0; } Capitulo 2. Gestión dinámica de memoria 2.1 Conceptos Básicos de Memoria Para iniciar este capítulo es importante que le demos un vistazo a la memoria de la computadora. Si usted ya sabe cómo funciona la memoria de la computadora, se puede saltar esta sección. Sin embargo, si no está seguro, le sugiero que la lea, le ayudará a comprender mejor ciertos aspectos de la programación. La computadora usa memoria de acceso aleatorio (RAM) para guardar información mientras está en funcionamiento. La RAM se encuentra en circuitos integrados o chips en el interior de la computadora. La RAM es volátil, lo que significa que es borrada y reemplazada con nueva información tan pronto como se necesita. La volatilidad también significa que la RAM “recuerda” solamente mientras la computadora está encendida, y pierde su información cuando se apaga la computadora. Cada computadora tiene una determinada cantidad de RAM instalada. La cantidad de RAM en un sistema se especifica por lo general en Megabytes (Mb) por ejemplo 256 Mb, 512 Mb, en ese orden de ideas se dice un byte es la unidad de medida fundamental de la memoria de una computadora, de los cuales se obtiene los Kilobytes, Megabytes, Gigabytes, siendo estos los más usados. Un kilobytes de memoria equivale a 1,024 bytes. 2.1.1 Tipo de datos y bytes requeridos en memoria Para darse una idea de que tantos bytes se necesitan para guardar determinados tipos de datos lo invito a que revise la siguiente tabla de espacios requeridos para guardar datos.

Datos Bytes requeridos

La letra X El número 100 El número 125.123 La frase Aprenda usted mismo Una página escrita completamente

1 2 4 21 3000 (Aproximadamente)

La RAM en la computadora está organizada en forma secuencial, un byte tras otro. Cada byte de memoria tiene una dirección única mediante la cual es identificado, una dirección que también lo

Page 14: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

14

distingue de todos los otros bytes de la memoria. Las direcciones son asignadas a la memoria en orden, comenzando en 0 y aumentando hasta llegar al límite del sistema. Para ampliar un poco más la conceptualización a cerca de los tipos de datos se define todo el posible rango de valores que una variable puede tomar al momento de ser ejecutada en el programa al igual que en toda la vida útil del propio programa. 2.2 Tipos de datos comunes Los tipos de datos más comunes utilizados frecuentemente en C++ son:

TIPO DATO ESPACIO MEMORIA RANGO

unsigned char 8 bits 0 a 255

Char 8 bits -128 a 127

short int 16 bits -32,768 a 32,767

unsigned int 32 bits 0 a 4,294,967,295

Int 32 bits -2,147,483,648 a 2,147,483,647

unsigned long 32 bits 0 a 4,294,967,295

Enum 16 bits -2,147,483,648 a 2,147,483,647

Long 32 bits -2,147,483,648 a 2,147,483,647

Float 32 bits 3.4 x 10-38 a 3.4 x 10+38(6 dec)

Doublé 64 bits 1.7 x 10-308 a 1.7*10+308(15 dec)

long doublé 80 bits 3.4 x 10-4932 a 1.1 x 10+4932

Void sin valor

2.2.1 Usaos de la Memoria RAM Alguna vez nos hemos hecho la siguiente pregunta ¿Para qué se usa la memoria RAM de la computadora? Tiene varios usos, pero solamente uno, el almacenamiento de datos, le interesa al programador. Los datos significan la información con la cual trabaja un programa. Ya sea que el programa esté trabajando con una lista de direcciones, monitoreando la bolsa de valores, manejando un presupuesto o cualquier otra cosa, la información (nombres, precios de acciones, gastos) es guardada en la RAM de la computadora mientras el programa esté ejecutando.

Page 15: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

15

Hasta el momento, la mayoría de los programas los hemos realizado definiendo variables, sin preocuparnos de que se realiza internamente en el computador, muchas veces en forma indiscriminada, es decir sin una verdadera depuración, pero existen ocasiones en que no sabemos cuanta memora necesitaremos para ejecución de determinado programa, por ejemplo si deseamos realizar un procesador de textos, no sabemos cuál va hacer la longitud del texto. Por eso a veces es necesario poder reservar memoria según se va necesitando. Además de esta forma nuestros programas aprovecharán mejor la memoria del computador en el que se ejecuten, usando sólo los recursos necesarios. Realmente la utilidad de asignación dinámica de memoria será aplicada en gran medida en los capítulos relacionados con las estructuras lineales. De acuerdo a lo anterior podemos definir dos tipos de variables: estáticas y dinámicas. 2.3 Tipos de variables Dependiendo el uso que se le dé a las variables por parte del programador, en una rutina o tarea específica se pueden identificar dos tipos de variables ellas son variables dinámicas y variables estáticas. 2.3.1 Variables estáticas Las variables estáticas como recordamos en los inicios de los fundamentos de programación, son aquellas que el programador les asigna memoria antes de la ejecución del programa o de una función, las variables estáticas se llaman mediante el nombre de la misma, que ha sido declarado por el programador. 2.3.2 Aplicación de las variables estáticas Veamos el código de un programa que hace uso de las variables estáticas Progra13.cpp #include <stdio.h> #include <iostream.h> #include <stdlib.h> #include <conio.h> int priNumero; /* Nuestras variables */ int segNumero; int suma; void main() { clrscr(); priNumero = 136; segNumero = 369; suma = priNumero + segNumero;

Page 16: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

16

cout<<"La suma es " <<suma; getch(); } 2.3.3 Análisis de Progra13.cpp En el código del programa se hace uso de tres variables de tipo entero que son variables estáticas ellas son: priNumero que se le asigna el valor 136, mientras que a la variable segNumero se le asigna el valor 369, de igual manera a la variable suma calcula el resultado de sumar el valor de las dos variables. 2.4 Variables dinámicas Las variables dinámicas deben su nombre al hecho de que pueden ser creadas y destruidas durante el tiempo de ejecución de un módulo. Para el manejo de variables dinámicas se hace indispensable la utilización de apuntadores, así como de funciones especiales para la asignación y liberación de la memoria correspondiente a dichas variables. 2.4.1 Aplicación de las variables dinámicas Veamos el código de un programa que hace uso de las variables dinámicas Progra14.cpp #include <iostream.h> #include <conio.h> void main() { int x, *a, **b, ***c ; // 1 clrscr(); a = &x ; // 2 *a = 100 ; // 3 b = &a ; // 4 **b += *a ; // 5 c = &b ; // 6 ***c += **b + *a ; // 7 cout << " *a=" << *a << " \n" ; cout << " **b=" << **b << " \n" ; cout << "***c=" << ***c << " \n" ; getch(); } 2.4.2 Análisis de Progra14.cpp En el listado del progra14.cpp se declaran tres variables apuntador de tipo entero ellas son *a, **b y **c el apuntador a almacena la dirección de la variable x, mientras que **b precedida de dos asteriscos indica que es una variable que apunta a un apuntador y ***c es un apuntador a apuntador a apuntador.

Page 17: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

17

2.5 Asignar y liberar espacios en memoria En el lenguaje C existen entre otras las funciones Malloc() y Free() para la asignación y liberación de memoria dinámicamente respectivamente. Cuando se ejecuta un programa, el sistema operativo reserva una zona de memoria para el código o instrucciones del programa y otra para las variables que se usan durante la ejecución. A menudo estas zonas son la misma zona, es lo que se llama memoria local. También hay otras zonas de memoria, como la pila, que se usa, entre otras cosas, para intercambiar datos entre funciones. El resto, la memoria que no se usa por ningún programa es lo que se conoce como "heap" o montón. Cuando nuestro programa use memoria dinámica, normalmente usará memoria del montón, y no se llama así porque sea de peor calidad, sino porque suele haber realmente un montón de memoria de este tipo. Profundizando un poco en la asignación dinámica, encontramos el operador sizeof, el cual determina el tamaño en bytes que se requiere en la asignación dinámica de memoria, ya sea por medio de los operadores New y Delete, o por las funciones Malloc y Free, de un arreglo o de cualquier otro tipo de datos, Ejemplo utilizar el operador sizeof en una función con el propósito de determinar el tamaño en bytes de un parámetro. 2.5.1 Aplicación del operador sizeof. En el siguiente listado se evidencia la aplicación y uso del operador sizeof en cada variable el cual devuelve el número de bytes dependiendo del tipo de variable. 2.5.2 Implementación del uso de sizeof Progra15.cpp #include <iostream.h> #include <conio.h> void main() { char c; short s; int i; long l; float f; double d; long double ld; int arreglo[20], * pt = arreglo; clrscr(); gotoxy(20,2); cout<<"valores utilizando sizeof para cada una de la varibles \n\n"; cout<<" variable c = " <<sizeof c; cout<<"\t tipo char = " <<sizeof (char);

Page 18: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

18

cout<<"\n variable s = " <<sizeof s; cout<<"\t tipo short = " <<sizeof (short); cout<<"\n variable i = " <<sizeof i; cout<<"\t tipo int = " <<sizeof (int); cout<<"\n variable l = " <<sizeof l; cout<<"\t tipo int = " <<sizeof (long); cout<<"\n variable f = " <<sizeof i; cout<<"\t tipo float = " <<sizeof (float); cout<<"\n variable d = " <<sizeof d; cout<<"\t tipo double = " <<sizeof (double); cout<<"\n variable ld = " <<sizeof ld; cout<<"\t tipo int = " <<sizeof (long double); cout<<"\n variable i = " <<sizeof i; cout<<"\t tipo int = " <<sizeof (int); getch(); return 0; } Capítulo 3. Operadores y funciones en la gestión de memoria 3.1 Operadores New y Delete El lenguaje C++ cuenta con dos operadores preconstruidos, ellos son: New y Delete, por esta razón no se requiere incluir ninguna librería o archivo de cabecera para utilizarlos. El operador New: Realiza una labor parecida a la de la función malloc(), asignando un bloque de memoria según sea requerido. El operador Delete: Libera un bloque de memoria asignada por New en tiempo de ejecución, de manera semejante a como lo hace la función free(). La sintaxis para el uso del operador new es: Apuntador = new tipo_de_dato; Este operador hace una petición al sistema operativo para que se le asigne un espacio de memoria, con tamaño de acuerdo al tipo de datos (recordemos la función sizeof), si este espacio está disponible, la operación regresa la dirección real que se otorga, en caso de no haber espacio regresa el valor de NULL (0), La sintaxis para el uso del operador delete es: delete apuntador; La ejecución de este operador provoca que se libere espacio, dejando como valor indefinido, es decir el sistema operativo lo considera como memoria disponible. Hay una regla de oro cuando se usa memoria dinámica, toda la memoria que se reserve durante el programa hay que liberarla antes de salir del programa. No seguir esta regla es una actitud muy irresponsable, y en la mayor parte de los casos tiene consecuencias desastrosas. No os fiéis de lo que diga el compilador, de que estas variables se liberan solas al terminar el programa, no siempre es verdad.

Page 19: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

19

3.1.1 Aplicación de los operadores New y Delete Veamos un ejemplo de utilización de los operadores de c++ utilizados para asignar y liberar memoria dinámicamente. “En el listado se declaran las variables index de tipo entero y los apuntadores point1 y point2 ambos de tipo entero. Progra16.cpp # include <iostream.h> main() { int index, *point1, *point2; point1 = &index; *point1 = 77; point2 = new int; *point2 = 173; cout <<"Los valores son " << index <<" " << *point1 << " "<< *point2 <<'\n'; point1 = new int; point2 = point1; *point1 = 999; cout <<"Los valores son " << index <<" " << *point1 << " "<< *point2 <<'\n'; delete point1; float *float_point1, *float_point2 = new float; float_point1 = new float; *float_point2 = 3.14159; *float_point1 = 2.4 * (*float_point2); delete float_point2; delete float_point1; char *c_point; c_point = new char; delete c_point; c_point = new char [sizeof(int) + 133]; delete c_point; } 3.1.2 Análisis de Progra16.cpp El resultado de la ejecución del listado llamado Progra16.cpp es: Los valores son 77 77 173 Los valores son 77 999 999

• En las primeras líneas del programa, se hace uso de los punteros tal y como se hacen también en C.

• point2 ilustra el uso del operador new. Este operador requiere un modificador que debe ser un

tipo. La parte new int significa que se crea un nuevo entero en la memoria, y devuelve la localización del entero creado. Esta localización es asignada a point2. La siguiente línea asigna 173 al entero al que apunta point2. Es importante distinguir entre point2, la localización del

Page 20: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

20

entero, y *point2, el entero. El puntero point2 apunta ahora a una variable entera que se ha reservado dinámicamente, y que puede utilizarse de igual forma que se hacía en C. Como ejemplo, se imprime el valor al que apunta.

• A continuación, se reserva memoria para una nueva variable, y point2 se refiere a la misma variable reservada dinámicamente a la que apunta point1. En este caso, la referencia a la variable a la que point2 apuntaba previamente se ha perdido, y nunca podrá ser utilizada o su memoria liberada. Sólo cuando se vuelva al sistema operativo se liberará la memoria que ocupaba. Por tanto, no debe utilizarse.

• Ya que el puntero point1 en sí no ha cambiado, apunta realmente al dato original. Este dato

podría referenciarse otra vez utilizando point1, pero no es una buena práctica de programación, ya que no hay garantía de lo que el sistema pueda hacer con el puntero o el dato. La localización del dato queda libre para ser reservada en una llamada subsiguiente, y será pronto reutilizada en cualquier programa.

• Ya que el operador delete está definido para no hacer nada si se le pasa un valor NULL, se puede liberar la memoria ocupada por un dato al que apunta un puntero NULL, ya que realmente no se está haciendo nada. El operador delete sólo puede utilizarse para liberar memoria reservada con el operador new. Si se usa delete con cualquier otro tipo de dato, la operación no está definida, y por tanto nada sucede.

• En el programa también declaramos algunas variables reales, y se realizan operaciones similares a las anteriores. De nuevo esto ilustra que en C++ las variables no tienen que ser declaradas al comienzo de cada bloque. Una declaración es una sentencia ejecutable y puede entonces aparecer en cualquier lugar en la lista de sentencias ejecutables.

• Finalmente, ya que el operador new requiere un tipo para determinar el tamaño de un bloque dinámicamente reservado, se muestra cómo reservar un bloque de tamaño arbitrario. Esto es posible utilizando la construcción de las últimas líneas del programa, donde un bloque de 37 caracteres de tamaño (37 bytes) es reservado. Un bloque de 133 bytes mayor que el tamaño de un entero se reserva posteriormente. Por tanto, el operador new se puede utilizar con la misma flexibilidad de la función malloc() de C.

• Cuando los datos reservados dinámicamente son borrados con delete, todavía quedan en memoria. Si repetimos la instrucción cout inmediatamente después de utilizar delete, veremos que todavía se conservan los valores. Si la repetimos de nuevo antes de dejar el programa, cuando el espacio que ocupaban debe haber sido sobre escrito, veremos que ya no es así. Incluso aunque el compilador nos dé los números correctos, no es una buena práctica pensar que esos datos están ahí todavía, porque en un programa dinámico largo la memoria se usará continuadamente.

• Las funciones estándar utilizadas en C para manejo dinámico de memoria, malloc(), calloc() y free(), también se pueden utilizar en C++ de la misma forma que en C. Los operadores new y delete no deben mezclarse con estas funciones, ya que los resultados pueden ser impredecibles. Si se está partiendo de código C, lo mejor es continuar utilizando las funciones en las nuevas líneas de programa. Si no es así, se deben utilizar los nuevos operadores, ya que se

Page 21: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

21

han construido como parte del lenguaje en sí, más que añadirse, y por tanto son más eficientes”1.

• Cuando se utiliza new para reservar memoria para un vector, el tamaño del vector se sitúa entre corchetes, siguiendo al tipo: int *intvector; intvector = new int [20]; y se libera: delete [ ] intvector;

3.2 Más sobre la Implementación de New y Delete El siguiente muestra un ejemplo de aplicación de los operadores new y delete. Progra17.cpp #include <iostream.h> void main() { int *apent; // Declara un apuntador a entero apent = new int ; // Reserva un bloque de memoria dinámica // de 2 bytes para manejarlo por medio de apent. *apent = 10 ; // Asigna el valor 10 al objeto apuntado por apent. cout << *apent ; // Despliega el contenido del objeto apuntado por apent. delete apent ; // Libera el espacio de memoria manejado por apent. } En el listado llamado Progra 17.cpp, se supone que la reservación será exitosa porque existe espacio suficiente en el montículo. Pero ¿quién asegura que el espacio requerido por new está disponible?. Para controlar esta situación y evitar un mensaje de error por parte del sistema en tiempo de ejecución, en el listado siguiente se propone una nueva versión del programa. 3.2.1 Implementación de New para verificación de memoria Progra18.cpp #include <iostream.h> #include <stdlib.h> // Para exit(). void main() { int *apent; // Declara un apuntador a entero if((apent = new int)==NULL)//Intenta reservar un bloque de memoria dinámica { // de 2 bytes por medio de apent. cout << "NO hay espacio suficiente\n"; exit(1); // Finaliza la ejecución del programa.

1 Fuente http://www.pablin.com.ar/computer/cursos/c1/allocate.html

Page 22: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

22

} *apent="10" ; // Asigna el valor 10 al objeto apuntado por apent. cout << *apent ; // Despliega el contenido del objeto apuntado por apent. delete apent ; // Libera el espacio de memoria manejado por apent. } 3.2.2 Análisis de Progra18.cpp Para crear un arreglo de 25 elementos de tipo double, en el montículo, puede escribirse: Double *dap ; dap = new double[25]; ó su forma equivalente: double *dap = new double[25]; En este ejemplo, se está declarando a dap como un apuntador a variables dinámicas de tipo doble; al tiempo que se le asigna el valor retornado por new. El valor retornado por new es la dirección del inicio de un bloque de memoria del tamaño requerido para almacenar 25 elementos de tipo double. En caso de que el montículo no disponga del espacio requerido, new retorna el valor NULL ( nulo ). 3.3 Funciones Malloc() y Free() 3.3.1 La función Malloc() Es una de las funciones de asignación de memoria del lenguaje de programación C (acrónimo de memory allocation). Cuando se usa malloc () se pasa la cantidad de bytes de memoria que se necesita. Malloc () encuentra y reserva un bloque de memoria del tamaño pedido y regresa la dirección del primer byte del bloque. No hay de qué preocuparse sobre que parte de memoria se usa, ya que esto es manejado automáticamente. La función malloc () regresa una dirección, y su tipo de retorno es un apuntador a tipo void. ¿Por qué void?. Un apuntador a tipo void es compatible con todos los tipos de datos. Como la memoria asignada por malloc () puede ser usada para guardar cualquiera de los tipos de datos de C, es adecuado el tipo de retorno void. 3.3.2 La función Free() Al igual que malloc(), free() es una función del lenguaje de programación C, utilizado para liberar la memoria asignada por malloc (). Al usar la función free () se debe tener en cuenta la regla de oro explicada en el apartado del operador delete “toda la memoria que se reserve durante el programa hay que liberarla antes de salir del programa” Al usar las funciones malloc () y free () se debe incluir el archivo de encabezado STDLIB.H 3.3.3 Ejemplos de aplicación Ejemplo 1 // asigna memoria para un arreglo de 50 enteros Int *números; Números = (int * ) malloc (50 * sizeof (int));

Page 23: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

23

Ejemplo 2 // asigna memoria para un arreglo de 10 valores float float *números; Números = (float * ) malloc (10 * sizeof (float)); 3.4 Aplicación a la asignación de memoria con Malloc() y Free() Progra19.cpp #include <conio.h> #include <stdlib.h> #include <stdio.h> void main() { clrscr(); int *p==NULL; int nbytes=100; p=(int *)malloc(nbytes); if(p=NULL) { cout<<"Insuficiente espacio en memoria\n"); return -1; } cout<<"se han asignado %d bytes de memoria\n", nbytes); free(p); getch(); } El resultado del programa muestra un mensaje en pantalla confirmando que se realizó la asignación dinámica de memoria con éxito. Ahora veamos otro ejemplo de aplicación de la asignación dinámica de memoria utilizando las funciones malloc () y free (), con un ingrediente adicional el uso de una estructura llamada persona que nos permita almacenar el registro de una persona, dos tipos de datos diferentes, un dato de tipo carácter que almacena el nombre de la persona y otro dato de tipo entero para almacenar la edad. 3.4.1 Implementación del uso de Malloc() y Free() Progra20.cpp // Listado de librerías o archives de cabecera #include<iostream.h> #include<stdlib.h> #include<conio.h> // Definición de la función principal void main() { clrscr();

Page 24: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

24

int n, i; // Definición de la estructura persona struct persona { char nombre[20]; int edad; }; // Definición del puntero p de tipo persona utilizado para reservar memoria persona *p; cout<<"PROGRAMA QUE GUARDA EL REGISTRO DE PERSONAS"<<"\n"; cout<<"\nNUMERO DE PERSONAS A INGRESAR : "; cin>>n; // Reserva de memoria dinámica a través de malloc () p =(persona *)malloc(sizeof(persona)); // El ciclo for usado para la entrada de los datos de la persona for(i=1;i<=n;i++) { cout<<"\nDIGITE EL NOMBRE " << i <<" : "; cin>>p[i].nombre; cout<<"DIGITE LA EDAD : "; cin>>p[i].edad; cout<<"\n"; } clrscr(); cout<<"LISTADO DE PERSONAS REGISTRADAS "<<"\n"; // El ciclo for usado para la impresión o visualización de los datos registrados for(i=1;i<=n;i++) { cout<<" NOMBRE : "<<p[i].nombre<<"\n"; cout<<" EDAD : "<<p[i].edad<<"\n\n"; } getch(); // La función free () libera la memoria asignada al apuntador p free (p); } 3.5 Otras funciones para asignar memoria dinámica Aparte de la función Malloc() tratada en la sección anterior, utilizada para la gestión dinámica de memoria, existen dos funciones adicionales para reservar memoria, ellas son: calloc() y realloc(). 3.5.1 La Funcione Calloc() A continuación se presentan el prototipo para la definición de la función Calloc(). void *calloc(size_t nmemb, size_t size);

Page 25: Capítulo 1. Apuntadores - Portal Aprende en Líneaaprendeenlinea.udea.edu.co/.../content/0/documentos/Apuntadores.pdf · Los apuntadores pueden manejarse en un arreglo, ... En el

25

“Cuando se usa la función malloc() la memoria no es inicializada (a cero) o borrada. Si se quiere inicializar la memoria entonces se puede usar la función calloc. La función calloc es computacionalmente un poco más cara pero, ocasionalmente, más conveniente que malloc. Se debe observar también la diferencia de sintaxis entre calloc y malloc, ya que calloc toma el número de elementos deseados (nmemb) y el tamaño del elemento (size), como dos argumentos individuales. Por lo tanto para asignar a 100 elementos enteros que estén inicializados a cero se puede hacer: int *ip; ip = (int *) calloc(100, sizeof(int) ); 3.5.2 La Función Realloc() Esta función intenta cambiar el tamaño de un bloque de memoria previamente asignado. El nuevo tamaño puede ser más grande o más pequeño. Si el bloque se hace más grande, entonces el contenido anterior permanece sin cambios y la memoria es agregada al final del bloque. Si el tamaño se hace más pequeño entonces el contenido sobrante permanece sin cambios. El prototipo de definición para la función realloc() es como se presenta a continuación. void *realloc(void *ptr, size_t size); Si el tamaño del bloque original no puede ser redimensionado, entonces realloc intentará asignar un nuevo bloque de memoria y copiará el contenido anterior. Por lo tanto, la función devolverá un nuevo apuntador (o de valor diferente al anterior), este nuevo valor será el que deberá usarse. Si no puede ser reasignada nueva memoria la función realloc devuelve NULL.” 2 Si para el ejemplo anterior, se quiere reasignar la memoria a 60 enteros en vez de 100 apuntados por ip, se hará; ip = (int *) realloc ( ip, 60*sizeof(int) ); Referencias Bibliográficas Unidad 1 KENNETH C, louden (2004). Lenguajes de programación. Mexico D.F : Thomson. AGUILAR, Luis (2003). Fundamentos de programación, algoritmos, estructura de datos y Objetos(tercera edición). España: McGRAW-HILL. AGUILAR, Luis (2000). Programación en C++, Algoritmos, estructura de datos y Objetos . España: McGRAW-HILL. DEYTEL Y DEYTEL (1999). Como programar C++(segunda Edición). Mexico D.F. : Prentice Hall. McGRAW-HILL. FARREL, Joyce (2000). Introducción a la programación lógica y diseño. Mexico D.F : Thomson. http://www.programacionfacil.com/estructura_de_datos/manejo_de_memoria http://www.conclase.net/c/fuentes.php?tema=3 http://www.ilustrados.com/publicaciones/EpZVVEZpyEdFpAKxjH.php

2 http://www.fismat.umich.mx/mn1/manual/node10.html consultado en Junio 18 de 2009