UC3M

Grado en Ing. Telemática/Sist. Audiovisuales/Sist. de Comunicaciones

Arquitectura de Sistemas

Septiembre 2017 - Enero 2018

6.9. 20 problemas de memoria dinámica

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
float4
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.

  1. ¿Qué tamaño tiene la estructura que se define en las líneas 6 a 11?

  2. ¿Por qué crees que se pone la línea 15? Prueba a comentarla y compila el programa.

  3. 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?

  4. 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?

  5. 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?

  6. 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.

  7. ¿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)

  8. ¿Dónde se copia el valor del puntero result al ejecutar la línea 48?

  9. ¿Cuántos bytes de memoria dinámica se utilizan en el programa de ejemplo?

  10. Clasifica las siguientes variables del programa ejemplo.

    Línea Variable Memoria global Heap Pila Sin memoria
    6-11struct bluetooth_info   
    13info   
    13new_info   
    19info_ptr   
    20i   
    23info_ptr->name   
    24info_ptr->strength   
    35src_ptr   
    37result   
    38i   
    40malloc(sizeof(struct bluetooth_info))   
    43result->strength   
  11. 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?

  12. 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?

  13. ¿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.

  14. 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é?

  15. 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?

  16. 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.

  17. 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) 
    {
    ...
    ...
    }
  18. 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?

  19. 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:

    1. ¿Qué valor debería tener justo antes de terminar la ejecución?

    2. 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?

  20. 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é?