Los siguientes 20 problemas asumen que conoces las llamadas al sistema para reservar y liberar memoria dinámica. Se suponen los siguientes tamaños de los tipos de datos básicos:
Tipo | Tamaño (bytes) |
---|---|
char , unsigned char
| 1 |
short int , unsigned short int
| 2 |
int , unsigned int , long
int , unsigned long int
| 4 |
float | 4 |
double , long double
| 8 |
Puntero de cualquier tipo | 4 |
Para la resolución de algunos de los siguientes problemas se considera el siguiente programa en C de ejemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <stdlib.h> #include <string.h> #define MAC_LENGTH 6 struct bluetooth_info { char *name; unsigned int strength; char mac[MAC_LENGTH]; }; struct bluetooth_info info, *new_info; struct bluetooth_info *duplicate(struct bluetooth_info *); int main(int argc, char **argv) { struct bluetooth_info *info_ptr; int i; info_ptr = &info; info_ptr->name = "My phone"; info_ptr->strength = 100; for (i = 0; i < MAC_LENGTH; i++) { info_ptr->mac[i] = 10; } new_info = duplicate(info_ptr); free(new_info); return 0; } struct bluetooth_info *duplicate(struct bluetooth_info *src_ptr) { struct bluetooth_info *result; int i; result = (struct bluetooth_info *)malloc(sizeof(struct bluetooth_info)); result->name = (char *)malloc(strlen(src_ptr->name) + 1); strcpy(result->name, src_ptr->name); result->strength = src_ptr->strength; for (i = 0; i < MAC_LENGTH; i++) { result->mac[i] = src_ptr->mac[i]; } return result; } |
Se recomienda que descargues este programa en tu área de trabajo para poder hacer cambios puntuales, compilarlo y ejecutarlo. Algunos de los siguientes problemas se pueden resolver de esta forma.
¿Qué tamaño tiene la estructura que se define en las líneas 6 a 11?
¿Por qué crees que se pone la línea 15? Prueba a comentarla y compila el programa.
En la línea 23 se realiza una asignación de una cadena de texto, ¿en qué tipo de memoria (global, heap, pila) crees que está esa cadena?
Reescribe las líneas 23 y 24 pero en lugar de utilizar la
variable info_ptr
utiliza directamente
info
. ¿Puedes escribir la función main
para que
haga exactamente lo mismo pero suprimiendo la variable
info_ptr
?
Busca qué hace exactamente la función strlen
que se utiliza en la línea 41. ¿Qué resultado devuelve para el caso del
programa? ¿Cuánta memoria se está reservando en esa invocación a
malloc
?
Busca qué hace la función strcpy
de la línea
42 y explica qué memoria se está modificando y dónde está para el caso del
programa de ejemplo.
¿Cómo accederías a la tercera letra de la cadena del campo
name
de la estructura a la que apunta result
en
la línea 43? (Puedes responder a esta pregunta insertando una línea que
imprima la respuesta y ejecutando el programa)
¿Dónde se copia el valor del puntero result
al ejecutar la línea 48?
¿Cuántos bytes de memoria dinámica se utilizan en el programa de ejemplo?
Clasifica las siguientes variables del programa ejemplo.
Línea | Variable | Memoria global | Heap | Pila | Sin memoria |
---|---|---|---|---|---|
6-11 | struct bluetooth_info | ||||
13 | info | ||||
13 | new_info | ||||
19 | info_ptr | ||||
20 | i | ||||
23 | info_ptr->name | ||||
24 | info_ptr->strength | ||||
35 | src_ptr | ||||
37 | result | ||||
38 | i | ||||
40 | malloc(sizeof(struct bluetooth_info)) | ||||
43 | result->strength |
La función duplicate
definida en las líneas
35 a 49, como su nombre indica, crea una estructura que es un duplicado de
la que apunta el parámetro dado. En la línea 29 del main
se
obtiene ese duplicado y se almacena en new_info
. ¿Cuál sería
el efecto de, en lugar de llamar a duplicate
simplemente
hacer new_info = info_ptr
?
La duplicación del campo name
de la
estructura a la que apunta el parámetro se realiza en las líneas 41 y
42. ¿Por qué no basta con poner simplemente result->name =
src_ptr->name
?
¿Cómo reservarías espacio para una tabla de 200 elementos
de la estructura struct bluetooth_info
y a la vez inicializar
su contenido todo al valor cero? Asigna la dirección de ese espacio al
puntero ptr
. Escribe la porción de código equivalente pero
utilizando malloc
.
En un programa en C hay que reservar espacio en memoria
dinámica para una tabla de 100 estructuras de datos previamente definidas,
pero no es preciso inicializarlas a ningún valor en particular. ¿Cuál de
las dos funciones malloc
y calloc
utilizarías?
¿Por qué?
Un programa define la siguiente estructura de datos en la que almacena una tabla de enteros y una tabla de letras pero de tamaño desconocido:
struct two_tables { int *int_table; char *char_table; };
Escribe una función que recibe dos enteros, reserva una
estructura de este tipo y con tablas de tamaños igual a los valores de los
enteros y devuelve el puntero a esta estructura. No es preciso inicializar
ningún dato. Pista: se requiere más de una llamada a
malloc
.
Escribe la función contraria, es decir, dado un puntero a una estructura de este tipo, libera la memoria que ocupa la estructura y las tablas.
¿Cómo reservarías espacio en memoria para una tabla de 100
estructuras de tipo struct two_tables
? ¿Cuánto espacio ocupa
en memoria?
Una aplicación gráfica mantiene una tabla con un conjunto de puntos que representa como números enteros. El número de puntos fluctua en un rango entre cero y un millón de puntos. Para no tener reservada memoria para un millón de puntos, la aplicación comienza por reservar espacio sólo para 1000 puntos con la línea:
points = (int *)malloc(1000 * sizeof(int));
La aplicación lleva la cuenta de los puntos que tiene almacenados, y al cabo de un rato ejecutando necesita almacenar más de 1000 puntos. ¿Qué operación sobre memoria dinámica necesitas ejecutar? ¿Qué parámetros utilizarías? Justifica tu respuesta.
Una aplicación almacena la información sobre contactos en una tabla con 100 estructuras como la siguiente:
struct contact { char *name; char *lastname; };
La variable table
almacena estos elementos y
su espacio ha sido reservado de forma dinámica (con malloc
o
calloc
). De forma análoga, para cada elemento, el espacio al
que apuntan los campos name
y lastname
también
ha sido reservado de forma dinámica. Escribe la función que recibe como
parámetro un puntero a una tabla de este tipo y un entero con su número de
elementos, y que libera toda la memoria ocupada por la tabla.
void nuke(struct contact *table, int size) { ... ... }
Supóngase la siguiente definición de estructura de datos, variables y código:
struct unit { struct unit *next; } *unit1, *unit2, *unit3; unit1 = (struct unit *)malloc(sizeof(struct unit)); unit2 = (struct unit *)malloc(sizeof(struct unit)); unit3 = (struct unit *)malloc(sizeof(struct unit)); unit1->next = unit2; unit2->next = unit3; unit3->next = NULL;
Escribe la función void borra(struct unit
*ptr)
tal que si se invoca como borra(unit1)
libera la
memoria reservada de las tres estructuras. ¿Tienes que cambiar algo en tu
función para que haga la misma operación para una longitud arbitraria de
la cadena de estructuras?
Supongamos que a lo largo de la ejecución de un programa
en C, cada vez que se invoca a malloc
o calloc
se incrementase una variable global entera a modo de contador inicializada
a cero, y cada vez que se invocase free
se
decrementase:
¿Qué valor debería tener justo antes de terminar la ejecución?
Y si un programa termina su ejecución y esta variable vale cero, ¿es esto suficiente para concluir que hace una gestión de memoria correcta?
Un programa en C manipula un número muy elevado de elementos de las dos siguientes estructuras de datos:
struct picture_info { char *name; char *location; char *note; int size; int latitude; int longitude; }; struct video_info { char *location; int *encoding int length; };
Todos los elementos se crean utilizando memoria dinámica y el código de la aplicación está perfectamente dividido en dos partes, cada una para manipular los elementos de una de las estructuras.
Al terminar la ejecución, el programa tiene porciones de memoria que se han reservado pero no liberado. Se utiliza una herramienta que supervisa esta ejecución y al final emite un informe en el que consta que 2345 porciones de memoria, todas ellas de 12 bytes, han sido reservadas pero no liberadas. ¿En qué parte del código empezarías a buscar la anomalía en la gestión de memoria y por qué?