1.
EL
MANEJO DE MEMORIA EN EL LENGUAJE C
Todos los objetos
tienen un tiempo de vida, es decir, el tiempo durante el cual se garantiza que
el objeto exista. Las variables globales y las variables locales declaradas con
el especificador static tienen
duración estática. Se crean antes de que el programa inicie su ejecución y se
destruyen cuando el programa termina. Las variables locales no static tienen duración automática. Se
crean al entrar al bloque en el que fueron declaradas y se destruyen al salir
de ese bloque. Duración asignada se refiere a los objetos cuya memoria se
reserva de forma dinámica. Como se explicó anteriormente, esta memoria se crea
y se debe liberar de forma explícita. Los arreglos de longitud variable de C99
son un caso especial. Tienen duración automática, con la particularidad de que
son creados a partir de su declaración.
La biblioteca
estándar de C proporciona las funciones malloc,
calloc, realloc y free para
el manejo de memoria dinámica. Estas funciones están definidas en el archivo de
cabecera stdlib.h.
Los datos se almacenan en uno de los tres segmentos de memoria que el
programador dispone. La zona estática para datos, que permite almacenar
variables globales durante la ejecución de un programa.
El stack que permite almacenar los argumentos y variables locales
durante la ejecución de las funciones.
El heap que permite almacenar variables adquiridas dinámicamente durante
la ejecución de un programa.
1.1 MANEJO ESTÁTICO DE LA MEMORIA.
La zona estática para datos, permite almacenar variables globales y
estáticas.
Si se encuentra una variable definida fuera de las funciones, se la
considera global; el compilador le asigna un espacio determinado y genera la
referencia para accesarla en la zona estática. El tamaño de las variables no
puede cambiarse durante la ejecución del programa, es asignado en forma estática.
El tiempo de vida de las variables de la zona estática es la duración
del programa.
Estas variables son visibles para todas las funciones que estén definidas
después de ellas.
Si se precede con la palabra static a una variable local a una función,
ésta también es ubicada en la zona estática, y existe durante la ejecución del
programa; no desaparece al terminar la ejecución de la función, y conserva su
valor, entre llamados a la función.
1.1.1
MEMORIA ESTÁTICA
Es
el espacio en memoria que se crea al declarar variables de cualquier tipo de
dato (primitivas [int,char...] o derivados [struct,matrices,punteros...]). Este
tipo de memoria no puede cambiarse una vez es declarada, y tampoco liberarse
cuando ya no es necesaria, consumiendo de esta forma recursos innecesariamente.
1.2 MANEJO DINÁMICO DE LA MEMORIA EN C
El compilador asigna un espacio determinado para las variables y genera
las referencias para accesar a las variables del stack y de la zona estática.
El tamaño de las variables no puede cambiarse durante la ejecución del
programa, es asignado en forma estática.
El tiempo de vida de las variables de la zona estática es la duración
del programa; las variables denominadas automáticas, o en la zona del stack,
existen durante la ejecución de la función que las referencia. Los frames en el
stack, son asignados y desasignados en forma dinámica durante la ejecución de
las funciones; pero en forma automática, el programador no tiene
responsabilidad en ese proceso.
En el heap el programador debe solicitar la asignación de espacio,
establecer las referencias entre el espacio asignado y las variables en las
otras zonas, liberar el espacio, desasignar las referencias. Cualquier
equivocación lleva a errores, en tiempo de ejecución, difíciles de depurar.
Este mecanismo permite al programador tener un mayor control de la
memoria, tanto en tamaño como en tiempo de vida, pero al mismo tiempo le da la
responsabilidad de administrarla correctamente. Un arreglo en la zona estática
debe ser definido con un tamaño determinado, el cual no puede cambiar durante
la ejecución del programa, sin embargo en el heap se puede solicitar un arreglo
del tamaño que se desee, siempre que no exceda el tamaño máximo asignado al
heap.
Escribir programas que manejen el heap es notablemente más difícil, por
esta razón lenguajes más modernos efectúan automáticamente la programación del
heap, y no le permiten al programador realizar esta tarea. Algunos errores de programas que manejan el
heap, compilan correctamente, sin embargo al ejecutarlos se producen errores
difíciles de depurar.
1.2.1 MEMORIA DINÁMICA
Es memoria que se
reserva en tiempo de ejecución. Su principal ventaja frente a la estática, es
que su tamaño puede variar durante la ejecución del programa.(En C, el
programador es encargado de liberar esta memoria cuando no la utilice más). El
uso de memoria dinámica es necesario cuando a priori no conocemos el número de
datos/elementos a tratar; sin embargo es algo más lento, pues es en tiempo de
ejecución cuando se determina la memoria a usar. En contrapartida la memoria
estática es más rápida ya que está disponible desde que se inicio el programa.
1.2.1.1 DIFERENCIAS, VENTAJAS Y DESVENTAJAS
La memoria
reservada de forma dinámica suele estar alojada en el heap o almacenamiento
libre, y la memoria estática en el stack o pila (con excepción de los objetos
de duración estática, que se verán más adelante, los cuales normalmente se
colocan en una zona estática de datos). La pila generalmente es una zona muy
limitada. El heap, en cambio, en principio podría estar
limitado por la cantidad de memoria disponible durante la ejecución del
programa y el máximo de memoria que el sistema operativo permita direccionar a
un proceso. La pila puede crecer de forma dinámica, pero esto depende del
sistema operativo. En cualquier caso, lo único que se puede asumir es que muy
probablemente dispondremos de menor espacio en la pila que en el heap.
Otra ventaja de la
memoria dinámica es que se puede ir incrementando durante la ejecución del
programa. Esto permite, por ejemplo, trabajar con arreglos dinámicos. Aunque en
C, a partir del estándar C99 se permite la creación de arreglos cuyo tamaño se
determina en tiempo de ejecución, no todos los compiladores implementan este
estándar. Además, se sigue teniendo la limitante de que su tamaño no puede
cambiar una vez que se especifica, cosa que sí se puede lograr asignando
memoria de forma dinámica.
Una desventaja de
la memoria dinámica es que es más difícil de manejar. La memoria estática tiene
una duración fija, que se reserva y libera de forma automática. En contraste,
la memoria dinámica se reserva de forma explícita y continúa existiendo hasta
que sea liberada, generalmente por parte del programador.
La memoria
dinámica puede afectar el rendimiento. Puesto que con la memoria estática el
tamaño de las variables se conoce en tiempo de compilación, esta información
está incluida en el código objeto generado, por lo cual el proceso es muy
eficiente. Cuando se reserva memoria de manera dinámica, se tienen que llevar a
cabo varias tareas, como buscar un bloque de memoria libre y almacenar la
posición y tamaño de la memoria asignada, de manera que pueda ser liberada más
adelante. Todo esto representa una carga adicional, aunque esto depende de la
implementación y hay técnicas para reducir su impacto.
1.2.1.2 RESERVA DE MEMORIA
.La función
g_malloc posibilita la reserva de una zona de memoria, con un número de bytes
que le pasemos como parámetro. Además, también existe una función similar
llamada g_malloc0 que, no sólo reserva una zona de memoria, sino que, además,
llena esa zona de memoria con ceros, lo cual nos puede beneficiar si se
necesita un zona de memoria totalmente limpia.gpointer g_malloc (gulong
numero_de_bytes );gpointer g_malloc0 (gulong numero_de_bytes );Existe otro
conjunto de funciones que nos permiten reservar memoria de una forma parecida a
cómo se hace en los lenguajes orientados a objetos.
1.2.1.3 LIBERACIÓN DE MEMORIA.
Cuando se
hace una reserva de memoria con g_malloc y, en un momento dado,
el uso de esa memoria no tiene sentido, es el momento de liberar esa
memoria. Y el sustituto de free es g_freeque, básicamente, funciona igual que
la anteriormente mencionada. Void g_free (gpointer memoria_reservada);
1.2.1.4
REALOJAMIENTO DE MEMORIA
En
determinadas ocasiones, sobre todo cuando
se utilizan estructuras de datos dinámicas, es necesario ajustar
el tamaño de una zona de memoria (ya sea para hacerla más grande o más
pequeña). Para eso, GLib ofrece la función g_realloc, que recibe un puntero a
memoria que apunta a una región que es la que será acomodada al nuevo tamaño
y devuelve el puntero a la nueva zona de memoria. El anterior puntero es
liberado y no se debería utilizar más: gpointer g_realloc (gpointer
memoria_reservada, gulong numero_de_bytes).
1.2.2 FUNCIONES
1.2.2.1 MALLOC
La función malloc reserva un bloque de memoria y
devuelve un puntero void al inicio de la misma. Tiene la siguiente definición:
void *malloc(size_t size);
donde el parámetro size especifica el número de bytes a reservar.
En caso de que no se pueda realizar la asignación, devuelve el valor nulo
(definido en la macro NULL), lo
que permite saber si hubo errores en la asignación de memoria.
Ej.:
int*puntero;
char*puntcarc;
puntero=(int*)malloc(4);
puntcarc=(char*)malloc(200);
A continuación se muestra un ejemplo de su uso:
int*i;
…
/* Reservamos la memoria
suficiente para almacenar un int y asignamos su dirección a i */
i =malloc(sizeof(int));
/* Verificamos que la
asignación se haya realizado correctamente */
if(i == NULL){
/* Error al intentar reservar
memoria */
}
Uno de los usos más comunes de la memoria
dinámica es la creación de vectores cuyo número de elementos se define en
tiempo de ejecución:
int*vect1, n;
printf("N£mero de
elementos del vector: ");
scanf("%d",&n);
/* Reservar memoria para
almacenar n enteros */
vect1 =malloc(n
*sizeof(int));
/* Verificamos que la
asignación se haya realizado correctamente */
if(vect1 == NULL){
/* Error al intentar reservar
memoria */
}
1.2.2.2 CALLOC
La función calloc funciona de modo similar a malloc, pero además de reservar memoria,
inicializa a 0 la memoria reservada. Se usa comúnmente para arreglos y
matrices. Está definida de esta forma:
void *calloc(size_t nmemb, size_t size);
El parámetro nmemb indica el número de elementos a
reservar, y size el tamaño de
cada elemento. El ejemplo anterior se podría reescribir con calloc de esta
forma:
int*vect1, n;
printf("N£mero de
elementos del vector: ");
scanf("%d",&n);
/* Reservar memoria para
almacenar n enteros */
vect1
=calloc(n,sizeof(int));
/* Verificamos que la
asignación se haya realizado correctamente */
if(vect1 == NULL){
/* Error al intentar reservar
memoria */
}
1.2.2.3 REALLOC
La función realloc
redimensiona el espacio asignado de forma dinámica anteriormente a un puntero.
Tiene la siguiente definición:
void *realloc(void *ptr, size_t size);
Donde ptr es el puntero a redimensionar,
y size el nuevo tamaño, en
bytes, que tendrá. Si el puntero que se le pasa tiene el valor nulo, esta
función actúa como malloc. Si la
reasignación no se pudo hacer con éxito, devuelve un puntero nulo, dejando
intacto el puntero que se pasa por parámetro. Al usar realloc, se debería usar un puntero temporal.
De lo contrario, podríamos tener una fuga de memoria, si es que ocurriera un
error en realloc.
Ejemplo de realloc usando
puntero temporal:
/* Reservamos 5 bytes */
void*ptr=malloc(5);
…
/* Redimensionamos el puntero
(a 10 bytes) y lo asignamos a un puntero temporal */
void*tmp_ptr
=realloc(ptr,10);
if(tmp_ptr== NULL){
/* Error: tomar medidas
necesarias */
}
else{
/* Reasignación exitosa.
Asignar memoria a ptr */
ptr=tmp_ptr;
}
Cuando se redimension la
memoria con realloc, si el nuevo
tamaño (parámetro size) es mayor
que el anterior, se conservan todos los valores originales, quedando los bytes
restantes sin inicializar. Si el nuevo tamaño es menor, se conservan los
valores de los primeros size bytes.
Los restantes también se dejan intactos, pero no son parte del bloque regresado
por la función.
1.2.2.4 FREE
La función free sirve
para liberar memoria que se asignó dinámicamente. Si el puntero es nulo, free no hace nada. Tiene la siguiente
definición:
void free(void *ptr);
El parámetro ptr es
el puntero a la memoria que se desea liberar:
int*i;
i =malloc(sizeof(int));
…
free(i);
Una vez liberada la memoria, si se quiere volver
a utilizar el puntero, primero se debe reservar nueva memoria con malloc o calloc:
int*i =malloc(sizeof(int));
…
free(i);
/* Reutilizamos i, ahora para
reservar memoria para dos enteros */
i =malloc(2*sizeof(int));
…
/* Volvemos a liberar la
memoria cuando ya no la necesitamos */
free(i);
2.
PALABRAS RESERVADAS
Las palabras reservadas son identificadores predefinidos (tienen un significado especial). En todos los lenguajes de programación existe un conjunto de palabras reservadas.
El
lenguaje de programación C se reserva el uso de algunas palabras como
identificadores internos clave para sus tipos de datos, estructuras de
control,sus instrucciones y otras funciones y características propias del
lenguaje. Por esta razón no es posible usarlos como identificadores de
variables, de funciones o de cualquier otro objeto. Los editores de texto
modernos las resaltan automáticamente para hacer los programas más legibles. A continuación una lista de dichas palabras.
auto - break - case - char - const - continue - default - do - double - else - enum - extern - float - for - goto - if - int - long - register - return - short - signed - sizeof - static - struct - typedef - union - unsigned - void - volatile – while.
No hay comentarios:
Publicar un comentario