ASIGNACIÓN DINÁMICA DE MEMORIA


Como se expresó en en capítulo de punteros, la variable referenciada por un puntero existe cuando se le asigna al puntero una dirección válida. Allí, se pudo apreciar varias formas de asignar esa dirección, empleando el operador &, asignando directamente el puntero a un arreglo o mediante un número entero que represente una dirección de memoria conocida y aplicándole a este número una conversión explícita (cast). Estas formas de asignar memoria al puntero se denomina "Asignación Estática", en las dos primeras, las variables referenciadas ocupan espacios de memoria que fueron asignados ya a otras variables, en el otro caso se trata de un espacio de memoria que es empleado por el sistema con una función específica.

No obstante, a pesar que los métodos anteriores son válidos, no son suficientes para el desarrollo de programas que emplean punteros. Muchas veces se requiere que el puntero apunte a un espacio de memoria que no esté ocupado por otras variavle o empleado por el sistema para algún fin, un espacio que pueda ser asignado en tiempo de ejecución del programa; que si en algún momento este espacio es insuficiente, se pueda reasignar la información que hay en él a otro espacio más grande y se pueda liberar el espacio asignado inicialmente para que sea empleado por otros punteros que lo requieran; que si la información contenida en el espacio de memoria ya no es reelevante, se pueda liberar el espacio de memoria para que éste sea empleado en otras partes del programa. Esta forma de gestionar la memoria se denomina "Asignación Dinámica de memoria"

El lenguaje C/C++ proporciona diferentes mecanismos que permiten gestar memoria dinámicamente, a continuación se detallan estas alternativas.

FUNCIÓN malloc   (prototipo en stdlib.h, alloc.h y malloc.h)

Esta función permite buscar un espacio de memoria libre del tamaño deseado y separarlo, de modo que la dirección del primer byte pueda ser asignada a un puntero. El prototipo de la esta función se muestra a continuación:

void * malloc (tamaño);

Donde tamaño indica la cantidad de bytes que se desea tenga el bloque de memoria. El tipo de dato void * indica que la dirección que devuelva la función no está relacionada con algún tipo de dato, esto se conoce con el nombre de "Dirección genérica" o "Puntero genérico", por esta razón cuando se emplee esta función, se deberá aplicar una operción cast al resultado devuelto por malloc.

Si por alguna razón, la función no pueda ubicar un espacio de memoria del tamaño deseado o el tamaño solocitado es cero, la función devuelve NULL.

Por ejemplo, si se tiene un puntero a un entero definido de la siguiente manera:

int * pt;

y se desea que la variable referenciada por el puntero almacene un número entero de tipo int, se debe determinar primero el tamaño del entero, esto debido a que a la función malloc se le debe proporcionar la cantidad de bytes del bloque. Si es cierto que uno puede colocar como argumento un número como 4, esto no es recomendable debido a que existe en el mercado una gran variedad de compiladores y estos no necesariamente definen el mismo tamaño para sus variables, por eso para evitar errores y que un mismo program se pueda ejecutar correctamente empleando diferentes compiladores, se recomienda el uso de la funció sizeof que recibe como argumento un tipo de dato y devuelve su tamaño en bytes; sizeof es una palabra reservada por lo que para usarla no se requiere de alguna biblioteca de funciones.

El código mostrado a continuación muestra en detalle esta operación:

#include <alloc.h>   // También se puede emplear stdlib.h o malloc.h

int main (void)
{  int * pt;

   pt = (int *) malloc( sizeof(int) );   // se asigna a pt la dirección de un bloque de memoria del tamaño de un int
   *pt = 37;   // ya se puede se asignar valores a la variable referenciada
   ···
}

Por otro lado, si a la expresión sizeof(int) se le multiplica por un valor entero, se habría definido un espacio de memoria denominado "Arreglo dinámico", esto quiere decir que el puntero podrá se majejdo como un arreglo común, con la diferencia que su tamaño se definió en tiempo de ejecución y no en tiempo de compilación como se define en los arreglos comunes. El siguiente ejemplo ilustra esta afirmación:

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

int main (void)
{  int * pt;

   int numElem;

   printf("Ingrese el número de datos del arreglo: ");
   scanf("%d%", &numElem);

   pt = (int *) malloc( numElem * sizeof(int) );   // se asigna a pt la dirección de un bloque de memoria del tamaño de un areglo de tipo int de tamaño numElem

   pt[2] = 177;   // se puede emplear pt como un arreglo común
   *pt = 88;    // es equivalente a hacer pt[0] = 88;
   *(pt+2) = 92;    // es equivalente a hacer pt[2] = 92;
   ···
}

Es bueno recordar que las variables que se definen como argumentos de una función, así como las variables locales se almacenan en un lugar de la memoria denominado "PILA" o "STACK", cuando se ejecuta una función estas variables son colocadas allí, y cuando la función termina , las variables son retiradas de allí automáticamente. Este no es el caso de la memoria asignada por la función malloc, en este caso el bloque de memoria está ubicado en una zona de memoria denominada "HEAP". Los bloques de memoria asignados allí no se liberan tan fácilmente, si el bloque fue separado dentro de una función, este bloque no se libera así la función termine, la única manera de liberar un bloque del HEAP se realiza cuando el programa termine o cuando se emplee una función o un operador que haga expícitamente esta operación.

FUNCIÓN free   (prototipo en stdlib.h, alloc.h y malloc.h)

Esta función permite liberar un espacio de memoria gestado por la función malloc. Para realizar esta operación, sólo hay que pasarle como parámetro da dirección de inicio del bloque. El prototipo de la esta función se muestra a continuación:

void free (dirección del bloque);
Por ejemplo:

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

int main (void)
{  int * pt;

   int numElem;    printf("Ingrese el númeor de datos del arreglo: ");
   scanf("%d%", &numElem);
   pt = (int *) malloc( numElem * sizeof(int) );    pt[2] = 177;
   *pt = 88;
   *(pt+2) = 92;
   free(pt);   // el bloque fue liberado
   ···
}

Es bueno indicar que, en el ejemplo anterior, luego de ejecutarse la función free, el puntero pt seguirá apuntando al bloque de memoria que le había sido asignado, sin embargo este bloque ha sido liberado, esto quiere decir que culaquier función malloc que se ejecute posterior a free podrá recibir ese bloque como respuesta, por lo tanto si intentamos utilizar la variable referenciada por pt, podríamos perder información requerida por otra variable.

FUNCIÓN realloc   (prototipo en stdlib.h, alloc.h y malloc.h)

Esta función permite trasladar la información contenida en un bloque de memoria a otro bloque de mayor tamaño. La función devuelve la dirección del byte de inicio del bloque y libera la memoria dada al bloque original. El prototipo de la esta función se muestra a continuación:

void * realloc (dirección del bloque original, nuevo tamaño);

Si por alguna razón no se pueda encontrar un bloque del tamaño pedido, la función realloc devuelve NULL y el bloque de memoria original no es liberado.

Esta función no debe emplearse para reubicar la información en un bloque más pequeño que en el que estaba, esto debido a que si bien es cierto se puede buscar el espacio solicitado y encontrarlo, al momento de trasladar la información, la función va a mover el contenido de todo el bloque original sobre el nuevo, al ser este último un espacio más pequeño, los datos que no quepan en el bloque serán ubcados en los bytes que le siguen, pudiendo destruir información de otras variables.

Por ejemplo, se desea ingresar una lista de valores y guardarlos en un arreglo y la cantidad de datos puede variar mucho de una corrida a otra. Para no desperdiciar mucha memoria definiendo un arreglo muy grande o que el arreglo se llene porque los datos son muchos, definiremos un arrglo dinámico de un tamaño no muy grande, luego si se llena incrementaremos su capacidad con la función realloc:

Grabar este ejemplo

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

void main (void)
{  int * datos;

   int capacidad = 50, numDatos = 0, valor;
   // Se genera un espacio inicial para el arrglo
   datos = (int *)malloc(capacidad * sizeof(int));

   printf("Ingrese valores enteros:\n");
   while(scanf("%d",&valor) != EOF)   // leer hasta terminar el archivo
    {  if (numDatos == capacidad)   // si el arrego se llena...
       {  capacidad += 20;
          datos = (int*)realloc(datos,capacidad * sizeof(int));   // ...aumentar la capacidad del arreglo
          if (datos == NULL)
           { printf("No se puede continuar,\n");
             printf("espacio insuficiente de memoria\n");
             return;
           }
       }
      datos[numDatos] = valor;
      numDatos++;
    }
   for (int i = 0; i < numDatos; i++)   // mostrar los datos leídos
      printf("%d) %d\n%quot;, i, datos[i]);
}

Debe tener en cuenta que no es bueno, que con el afán de no desperdiciar memoria, se defina inicialmente el arrglo dinámico de tamaño 1, y que cada vez que se ingrese un nuevo dato se amplien el tamaño del arreglo en una unidad. La razón de esto es que el programa se tornaría altamente ineficiente debido a que realloc busca un nuevo espacio de memoria y luego copia el bloque original en él, y esto toma tiempo; no es correcto hacer perder este tiempo por cada dato ingresado.

OPERADOR new

Las funciones malloc, free y realloc fueron definidas para el lenguaje C. Cuando se diseña el lenguaje C++ se le agrega al núcleo del compildor un nuevo operador (como el +, el /, el &, etc.), denominado new. Este operador permite realizar la misma tarea que malloc, sin embargo en su implementación se contempla la asignación de espacios en función del tipo de dato y no en función del tamaño requerido como se hace con malloc. El modo de utilizar este operador es el siguiente:

puntero = new tipo; o también
puntero  = tipo [ n ];

Ambos métodos reservan un bloque continuo de memoria de tamaño igual al tipo o al tipo multiplicado por n.

Ejemplo:

#include <stdio.h>

int main (void)
{  int * pt;
   unsigned char * cad;
   int numElem;

   pt = new int;   // se asigna a pt la dirección de un bloque de memoria del tamaño de un int

   printf("Ingrese el número de datos del arreglo: ");
   scanf("%d%", &numElem);

   cad = new unsigned char [numElem];   // se asigna a cad la dirección de un de un areglo de tipo unsigned char de tamaño numElem

   ···
}

OPERADOR delete

Como se vió con new, delete es un operador de C++. La tarea que realiza es similar a la de la función free, la de liberar el espacio de memoria asignado al puntero. Sólo se podrá emplear el operador delete en aquellos punteros que fueron inicializados con new. El modo de utilizar este operador es el siguiente:

delete  puntero;     o también
delete [ ]  puntero;

La primera forma se emplea paar liberar la memoria de variables simples, mientras que la segunda para arreglos. El siguiente ejemplo ilustra esta afirmació:

#include <stdio.h>

void main (void)
{  int * pt1, * pt2;

   pt1 = new int;   // se asigna a pt1 la dirección de un bloque de memoria del tamaño de un int
   *pt1 = 139;

   pt2 = new int [5];   // se asigna a pt2 la dirección de un de un areglo de tipo int de tamaño 5
   ···

   delete pt1;
   delete [] pt2;
   ···
}

Ejemplo

El programa presentado a continuación muestra cómo se puede leer un texto desde la consola y almacenarlo en un espacio de memoria exacto para el tamaño del texto, sin desperdiciar memoria.

El problema aquí se presenta porque no se puede saber qué tamaño de texto ingresará el usuario, por lo que no se puede emplear un arreglo simplemente. Es por esta razón que vamos a tener que aprovechar las propiedades de las variables locales para solucionar el problema.

El texto no será leído directamante, sino que se leerá a través una función, la cual definirá un arreglo, de tamaño relativamente grande, en donde se colocará el texto leío, luego se trasladará este último a un espacio de memoria gestado dinámicamente del tamaño del texto.

Cuando la función termine, el arreglo desaparecerá automaticamente, y más bien se devolverá la dirección gestada.

Grabar este ejemplo

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

char *leeTexto(void);

void main (void)
{  char *nombre;

   printf("Ingrese un nombre: ");
   nombre = leeTexto ();

  printf("\nNombre leido: %s\n",nombre);
}

char *leeTexto(void)
{  char buffer[200]; //arreglo de gran tamaño    char *pt;
   int longTexto;

   gets(buffer);   //almacena el nombre en el arreglo

   longTexto = strlen(buffer);
    if (longTexto==0) return NULL;

   pt = (char*)malloc(sizeof(char) * (longTexto + 1));
         // Se genera el espacio exacto
         // +1 para que almacene el 0 al final de texto

   strcpy(pt,buffer);
   // las dos instrucciones anteriores se pueden reemplazar por:
   // pt = strdup(buffer);

   return pt;
   // aqui de destruye el buffer y todas las variables locales
}
ARREGLOS DE APUNTADORES

En principio, un arreglo de apuntaores viene a ser un arreglo común y corrienente con la particularidad que cada elemento del arreglo es un puntero.
La manera de declarar un arreglo de apuntadores es la siguiente:

int * lista[20];
char * nombres[50];
Como cada elemento del arreglo es un puntero, éste puede apuntar tanto a un solo elemento como a un área de memoria, la figura siguiente muestra cómo se pueden organizar los datos en estas estructuras.

La estructura creada bajo estos arreglos son estructuras altamente eficientes, en primer lugar porque el manejo de cada elemento se hace de la misma forma que se maneja una matriz de dos dimensiones, esto se puede demostrar de la siguiente manera:

Si definimos un puntero pt como : int * pt; y hacemos que apunte al mismo lugar que un elemento de lista, de la siguiente manera: pt = lista[1];, luego podremos manipular el valor de 437 mediante la expresión pt[2]. Pero como pt y lista[1] apuntan al mismo lugar, podemos emplear cualquiera de los dos para llegar al elemento, entonces reemplazando lista[1] por pt tendremos : lista[1][2], nomenclatura que corresponde a la de una matriz.

Sin embargo, la gran diferencia con una matriz es que en esta estructura, cada fila puede tener una cantidad diferente de elementos y por lo tanto no se desperdicia memoria como en una matriz en la que hay que definir el número de fila y columnas en el momento de declaralas.

Pero no sólo es el ahoro de memoria la ventaja de emplear este tipo de estructura de datos, el aspecto más importante del uso de los arreglos de punteros radica en la manipulacón de los datos. Supongamos que luego de definir una matriz como char nombre[50][20] y de llenarla con datos como nombres de personas, la queremos ordenar alfabéticamente. Cuando el algoritmo de ordenación llegue a detectar que un elemento no está en el lugar correcto con respecto a otro los tendrá que intercambiar, en el caso de la matriz se tendrá que rotar todos los caracteres de las fila involucradas empleando un arreglo auxiliar como se muestra a continuación:

Si por otro lado se emplea un arreglo de punteros como char *nombe[50];; en el momento de intercambiar, sólo se tendrá que intercambiar las direcciones a las que apunta los relementos del arreglo, para lo cual se hará uso de un puntero auxiliar como char *aux;. Esta operación representa un traslado de 4 bytes (en el peor de los casos) por cada uno de los tres punteros involucrados, en lugar de todos los bytes que contienen las cadenas de texto. Est se puede observar en la figura siguiente:

Ejemplo

El siguiente programa muestra la manera en que se puede leer una serie de nombres (cadenas de caracteres), almacenarlas en un arreglo de punteros, ordenarlos alfabéticamente y finalmente imprimirlos ordenados. Todo se hará definiendo un arreglo de punteros.

Grabar este ejemplo

/* Programa que emplea un arreglo de punteros para leer una serie
    de nombres (cadenas de caracteres), luego los ordena y finalmente
    imprime la lista ordenada
*/

#include <stdio.h>
#include <string.h>
// Encabezados de funciones:
   void leeDatos(char *[ ], int &);
   void ordenar (char *[ ], int, int);
   void imprimir(char *[ ], int);
   char * leeCadena(void);
   void cambiar(char *[ ], int, int);

void main (void)
{ int numDatos;               // Indicar  el número de datos leídos
  char *nombre[100];      // Cada elemento del arreglo apunta
                                    // a un arreglo de caracteres


  leeDatos(nombre, numDatos);
  ordenar(nombre, 0, numDatos-1);    // Se empleará el método QuickSort
  imprimir(nombre, numDatos);
}

void leeDatos(char *nombre[ ], int &numDatos)
{ numDatos = 0;
  while (1)
   { nombre[numDatos] = leeCadena();
     if (nombre[numDatos]==NULL) break;
     numDatos++;
   }
}

void ordenar (char *nombre[ ], int izq, int der)
// Ordena por el método QuickSort
  int limite;       // Marca el límite entre los mayores y menores que el pivot
  if (izq >= der) return;
  cambiar(nombre, izq, (izq+der)/2);   //Pone el pivot en la primera posición

  limite = izq;
  for (int i = izq+1; i <= der; i++)
     if ( strcmp( nombre[i], nombre[izq]) < 0)
           cambiar(nombre, ++limite, i);

    cambiar(nombre, izq, limite);

    ordenar(nombre, izq, limite-1);
    ordenar(nombre, limite+1, der);
}

void imprimir(char *nombre[ ], int numDatos)
{ int i;

  for (i = 0; i < numDatos; i++)
      printf("%3d) %s\n", i + 1, nombre[i]);
}

char * leeCadena(void)
{ char buffer[300], *auxSt;
  int longSt;

  gets(buffer);
  longSt = strlen(buffer);
  if (longSt)    // Si su longitud es diferente de cero
   { // se reserva un espacio exacto de memoria y se copia
     auxSt = new char[longSt + 1];
     strcpy(auxSt, buffer);
     return auxSt;
   }

  return NULL;
}

void cambiar(char *nombre[ ], int i, int j)
{ char *aux;

  // se intercambian sólo los punteros
  aux = nombre[i];
  nombre[i] = nombre[j];
  nombre[j] = aux;
}

PUNTEROS A PUNTEROS o PUNTEROS DOBLES

Un puntero doble es un puntero cuya variable refernciada es otro puntero, el que a su vez apunta a otra variable referenciada la cual contien el valor final del dato.

Un puntero a puntero se define de la siguiente manera:

int ** pt;

En ese instante el puntero pt no tiene una dirección válida donde apuntar (no existe la variable refrenciada), su estado está como se muestra en la sigiuente figura:

Si en ese momento realizamos una operación de asignación dinámica de memoria a pt como se muestra a continuación:

pt = new int *;
O la instrucción equivalente:
pt = (int **) malloc(sizeof(int*));

Se habrá asignado a pt la dirección de una variable referenciada, la cual es un puntero. Este nuevo puntero aun no tiene una dirección válida donde apuntar. Su estado se muestra a continuación:

Luego se tendrá que realizar la siguiente operación para asignar la dirección a la variable referenciada:

*pt = new int;    o    *pt = (int *) malloc(sizeof(int));

Y luego la opreción:    **pt = 57;

Con lo que los punteros quedarán finalmente en el siguiente estado:

Esta forma de manejar los punteros dobles, si bien es cierto que teóricamente es válida, no tiene mucha utilidad. Si por otro lado en lugar de asignar a los punteros sólo una variable referenciada le asignamos un área de memorias, el resultado será muy diferente. Observe el siguiente código:

int tam1, tam2, i;
int **pt;
...
scanf("%d", &tam1);
pt = (int**)malloc(sizeof(int*)*tam1);  // Ahora pt se comportará como un arreglo
for (i = 0; i < tam1; i++)
 { scanf("%d",&tam2);
   pt[ i ] = (int*)malloc(sizeof(int)*tam2);  // Ahora pt[i] se comportará también como un arreglo
 }

La ejecución de este código formará una estructura como la que s emuestra a continuación:

Esta estructura se comporta de la misma manera que los arreglos a punteros definidos en los puntos anteriores, esto quiere decir que un elemento de la estructura se puede manejar como pt[i][j]. Sin embargo a diferencia de estos, el tamaño del arreglo se define en tiempo de ejecución , esto es una ventaja ya que así no se desperdiciaría memoria en ambas dirmensiones.

A continuación se muestra el mismo ejemplo que se presentó con los arreglos de punteros pero ahora empleando punteros dobles.

Grabar este ejemplo

/* Programa que emplea un doble puntero para leer una serie de
    nombres (cadenas de caracteres), luego las ordena y finalmente
    imprime la lista ordenada. La cantidad de elementos en cada
    dimensión se asignará en forma exacta.
*/


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

// Encabezados de funciones:
     void leeDatos(char **&, int &);   // El puntero debe pasar por referencia
     // porque es dentro de la función donde se le asignará la dirección de la
     // estructura formada


     void ordenar (char **, int, int);
     void imprimir(char **, int);
     char * leeCadena(void);
     void cambiar(char**, int, int);

void main (void)
{ int numDatos;      // Indicar  el número de datos leídos
  char **nombre;    // puntero doble, aun no apunta a una dirección válida

  leeDatos(nombre, numDatos);
  ordenar(nombre, 0, numDatos-1);  // Se empleará el método QuickSort
  imprimir(nombre,numDatos);
}

void leeDatos(char **&nombre,  int &numDatos)
{  char *buffer[500];      //arreglo estático temporal, se destruirá cuando se
                                      // termine la función

    numDatos = 0;
    while    (1)
    { buffer[numDatos] = leeCadena( );
      if (buffer[numDatos] == NULL) break;
      numDatos++;
    }

    // Ahora se le asigna a "nombre" la cantidad exacta de elementos leídos
    nombre = new char*[numDatos];

    // Finalmente le asignamos a cada elemento de "nombre" las direcciones
    // a las que apuntan los elementos de buffer

    for(int i = 0; i<numDatos; i++)
          nombre[i] = buffer[i];
}

// Todo lo demás es igual, incluso se puede mantener los parámetros como
// char *nombre[ ] en lugar de char **nombre


void ordenar (char **nombre, int izq, int der)
{  // Ordena por el método QuickSort
    int limite;    // Marca el límite entre los mayores y menores que el pivot

    if (izq >= der)  return;

    cambiar(nombre, izq, (izq+der)/2);  //Pone el pivot en la primera posición

    limite = izq;
    for (int i = izq+1; i <= der; i++)
        if (strcmp(nombre[i], nombre[izq]) < 0 )
            cambiar(nombre, ++limite, i);

    cambiar(nombre, izq, limite);
    ordenar(nombre, izq, limite - 1);
    ordenar(nombre, limite + 1, der);
}

void imprimir(char **nombre, int numDatos)
{  int i;
   for (i = 0; i < numDatos; i++)
       printf("%3d) %s\n", i + 1, nombre[i]);
}

char * leeCadena(void)
{  char buffer[300], *auxSt;
   int longSt;

   gets(buffer);
   longSt = strlen(buffer);
   if (longSt)    // Si su longitud es diferente de cero
   {  // se reserva un espacio exacto de memoria y se copia
      auxSt = new char[longSt + 1];
      strcpy(auxSt, buffer);
      return auxSt;
   }
   return NULL;
}

void cambiar(char**nombre, int i, int j)
{  char *aux;
   // se intercambian sólo los punteros
   aux = nombre[i];
   nombre[i] = nombre[j];
   nombre[j] = aux;
}

PUNTEROS COMO PARÁMETROS

Pasar un puntero como parámetro a una función es una tarea que debe hacerse con mucho cuidado, porque si el concepto no se tiene claro, se puede incurrir en errores muy serios que son a su vez muy difíciles de detectar a la hora de depurar el programa.

Obserbe el siguiente programa:

Grabar este ejemplo

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

void f1( char *);
void f2( char * &);

void main (void)
{  char *cad1, *cad2;
   cad1 = (char *) malloc(80*sizeof(char));
   strcpy(cad1, ”FRANCISCA/RODRIGUEZ/RIVASPLATA”);

   cad2 = (char *) malloc(80*sizeof(char));
   strcpy(cad2, ”FRANCISCA/RODRIGUEZ/RIVASPLATA”);

   f1(cad1);      f2(cad2);

   printf("Cadena 1: %s\n", cad1);
   printf("Cadena 2: %s\n", cad2);
}

void f1(char * cad)
{  strcpy(cad, "ANA/PAZ/HARO");
   cad = (char *) malloc(20*sizeof(char));
   strcpy(cad, "ALICIA/CASTRO/GOMEZ");
}

void f2(char * &cad)
{  strcpy(cad, "ANA/PAZ/HARO");
   cad = (char *) malloc(20*sizeof(char));
   strcpy(cad, "ALICIA/CASTRO/GOMEZ");
}

Al ejecutar este programa podemos observar que se imprimirá lo siguiente:

   Cadena 1: ANA/PAZ/HARO
   Cadena 2: ALICIA/CASTRO/GOMEZ

La razón para este comportamiento se debe a que a pesar que estamos trabajando con punteros, éstos pasan a las funciones como parámetros por valor, a menos que se coloque explícitamente el símbolo & que lo defina como un parámetro por referencia.

Las figuras que se muestran a continuación, ilustran esta afirmación:

ESTADO DE LOS PUNTEROS:
1)  Declaración de los punteros:   char *cad1, *cad2;

     A los punteros cad1 y cad2 aun no se les asignó una dirección donde apuntar.

2)  Asignación dinámica de memoria y el llenado de estos espacios:
               cad1 = (char*)malloc(80*sizeof...
               strcpy(cad1, "FRANCISCA/ROD...

     Ambos punteros apuntan a direcciones de memoria a los que se les asignó las cadenas de caracteres.

3)  Ejecucición de las funciones f1(cad1); y f2(cad2); :

     En el caso de f1 se crea una nueva variable en la pila que recibe la dirección asignada a cad1. en el caso de f2, cad recibe la referencia de cad2.

4)  Se cambia el contenido de las variables referenciadas:
               strcpy(cad, "ANA/PAZ/HARO");

5)  Se modifica la dirección apuntada por cad y se llena con otra información:
               cad = (char*)malloc(80*sizeof...
               strcpy(cad, "ALICIA/CAS...

     En el primer caso cad apunta a otro espacio de memoria, mientras que cad1 sigue apuntando a la dirección inicial.
     En el segundo caso cad y cad2 cambian su dirección apuntada.

6)  Fin de las funciones f1 y f2:
     Las variables cad son eliminadas de al pila.
     El estado final de cad1 y cad2 dan lugar a la salida impresa.



Volver a contenidos AtrásSiguiente