SDK Multiplataforma en C logo

SDK Multiplataforma en C

Memoria

❮ Anterior
Siguiente ❯

Manipulación genérica de bloques de memoria y manejo del heap.


Funciones

byte_t*bmem_malloc (...)
byte_t*bmem_realloc (...)
byte_t*bmem_aligned_malloc (...)
byte_t*bmem_aligned_realloc (...)
voidbmem_free (...)
voidbmem_set1 (...)
voidbmem_set4 (...)
voidbmem_set8 (...)
voidbmem_set16 (...)
voidbmem_set_u32 (...)
bool_tbmem_is_zero (...)
voidbmem_set_zero (...)
voidbmem_zero (...)
voidbmem_zero_n (...)
voidbmem_copy (...)
voidbmem_copy_n (...)
voidbmem_move (...)
bool_tbmem_overlaps (...)
voidbmem_rev (...)
voidbmem_rev2 (...)
voidbmem_rev4 (...)
voidbmem_rev8 (...)
voidbmem_revcopy (...)
voidbmem_swap (...)
voidbmem_swap_type (...)
voidbmem_shuffle (...)
voidbmem_shuffle_n (...)

Desde el punto de vista del programador, el acceso a memoria se realiza por medio de variables y se manipula a través de los operadores del lenguaje (+, -, *, =, ...) y siempre de la misma forma, independientemente de como se hayan creado las variables o en que zona de memoria se alojen. Dentro de bmem.h disponemos de varias funciones para realizar copias, asignaciones o comprobaciones de bloques genéricos de memoria. En este módulo también se definen funciones para la manipulación de la memoria dinámica (Heap).

  • Utiliza bmem_malloc para reservar un bloque de memoria dinámica.
  • Utiliza bmem_free para liberar un bloque de memoria dinámica.
  • Utiliza bmem_copy para copiar el contenido de dos bloques de memoria, previamente reservados.

1. Segmento Stack

La memoria de un programa C compilado y en ejecución se divide en varios segmentos. Uno de ellos es el stack, un espacio de tamaño variable pero limitado, donde se guardan las variables locales y las llamadas a función (call stack). Va creciendo y encogiendo a medida que el proceso entra y sale de ámbitos o funciones (Figura 1). Es gestionado automáticamente por el compilador como una estructura LIFO Last-in First-out, por lo que pasa desapercibido la mayor parte del tiempo, ya que no requiere atención extra por parte del programador. Nos percatamos de su existencia al recibir el error Stack Overflow, normalmente provocado por recursividad infinita o la reserva de vectores C muy grandes (Listado 1). El depurador nos permite inspeccionar el estado de la pila en cualquier instante de la ejecución Depurando el programa.

Esquema del segmento stack en varios momentos de la ejecución.
Figura 1: Estado del stack en diferentes puntos del programa.
Listado 1: Dos sencillos casos que provocan el desbordamiento de la pila.
1
2
3
int func(int n) { func(n); } // Stack Overflow

float v[2000000]; // Stack Overflow

Si bien el uso del stack es ideal debido a su gran rendimiento, seguridad y facilidad de uso, en ocasiones se queda corto. Por un lado hay que prever en tiempo de diseño la cantidad de memoria necesaria y definirla estáticamente (pe. struct Product pr[100];), algo muy poco flexible a la hora de construir aplicaciones de verdad. Por otro lado, las variables se destruyen al cerrar un ámbito o salir de una función, lo que impide compartir datos de manera global.


2. Segmento Heap

El heap es una zona de memoria que el proceso puede solicitar bajo demanda, a través de llamadas al sistema. Es complementaria al stack y se caracteriza por:

  • Se puede acceder de forma global, desde cualquier punto del programa a través de un puntero.
  • La cantidad de memoria disponible es prácticamente ilimitada.
  • Es menos eficiente que el stack.
  • Requiere gestión. Los sistemas operativos proveen funciones para solicitar bloques de memoria dinámica (HeapAlloc(), sbrk()), siendo responsabilidad del proceso, o mejor dicho del programador, liberar estos bloques cuando ya no se necesiten.

Como las reservas y liberaciones pueden realizarse en cualquier orden se produce una fragmentación interna a medida que avanza el programa (Figura 2). Aquí entraría en juego el denominado gestor de memoria, que son algoritmos que permiten optimizar el uso del heap compactándolo y reutilizando los bloques liberados. La librería estándar de C proporciona las conocidas funciones malloc()/free(), que implementan un gestor de memoria genérico a través de llamadas al sistema.

Esquema de la fragmentación del segmento heap a medida que avanza la ejecución del programa.
Figura 2: Fragmentación del heap durante la ejecución del proceso.
NAppGUI implementa su propio gestor/auditor de memoria dinámica Heap - Gestor de memoria muy optimizado para servir numerosas de peticiones de pequeño tamaño, que es lo que demandan las aplicaciones en gran medida. bmem_malloc/bmem_free conectan con el sistema operativo a través de llamadas al sistema y no deberían utilizarse directamente.

bmem_malloc ()

Reserva un bloque de memoria con la alineación por defecto sizeof(void*).

byte_t*
bmem_malloc(const uint32_t size);
size

Tamaño en bytes del bloque.

Retorna

Puntero al nuevo bloque. Debe ser liberado con bmem_free cuando ya no sea necesario.

Observaciones

Utiliza Heap - Gestor de memoria para reservas más eficientes y seguras.


bmem_realloc ()

Realoja un bloque de memoria existente debido a la expansión o reducción del mismo. Garantiza que se conserva el contenido previo del bloque min(size, new_size). Intenta hacerlo sin mover memoria (in situ), pero si no es posible busca una nueva zona. También garantiza la alineación por defecto sizeof(void*) si hay que reservar un nuevo bloque.

byte_t*
bmem_realloc(byte_t *mem,
             const uint32_t size,
             const uint32_t new_size);
mem

Puntero al bloque original a realojar.

size

Tamaño en bytes del bloque original mem.

new_size

Nuevo tamaño requerido, en bytes.

Retorna

Puntero al bloque realojado. Será el mismo que el puntero original mem si la reubicación "in-situ" ha tenido éxito. Debe ser liberado con bmem_free cuando ya no sea necesario.

Observaciones

Utiliza Heap - Gestor de memoria para reservas más eficientes y seguras.


bmem_aligned_malloc ()

Reserva un bloque de memoria con alineación.

byte_t*
bmem_aligned_malloc(const uint32_t size,
                    const uint32_t align);
size

Tamaño en bytes del bloque.

align

Alineación. Debe ser potencia de 2.

Retorna

Puntero al nuevo bloque. Debe ser liberado con bmem_free cuando ya no sea necesario.

Observaciones

Utiliza Heap - Gestor de memoria para reservas más eficientes y seguras.


bmem_aligned_realloc ()

Igual que bmem_realloc, pero garantiza una alineación concreta.

byte_t*
bmem_aligned_realloc(byte_t *mem,
                     const uint32_t size,
                     const uint32_t new_size,
                     const uint32_t align);
mem

Puntero al bloque original a realojar.

size

Tamaño en bytes del bloque original mem.

new_size

Nuevo tamaño requerido, en bytes.

align

Alineación. Debe ser potencia de 2.

Retorna

Puntero al bloque realojado.

Observaciones

Utiliza Heap - Gestor de memoria para reservas más eficientes y seguras.


bmem_free ()

Libera la memoria apuntada por mem, previamente reservada por bmem_malloc, bmem_realloc o sus equivalentes con alineación.

void
bmem_free(byte_t *mem);
mem

Puntero al bloque de memoria a liberar.

Observaciones

Utiliza Heap - Gestor de memoria para reservas más eficientes y seguras.


bmem_set1 ()

Rellena un bloque de memoria con la misma máscara de 1-byte.

void
bmem_set1(byte_t *dest,
          const uint32_t size,
          const byte_t mask);
dest

Puntero al bloque de memoria.

size

Tamaño en bytes del bloque dest.

mask

Máscara.


bmem_set4 ()

Rellena un bloque de memoria con la misma máscara de 4-bytes.

void
bmem_set4(byte_t *dest,
          const uint32_t size,
          const byte_t *mask);
1
2
3
4
byte_t mblock[10];
byte_t mask[4] = "abcd";
bmem_set4(mblock, 10, mask);
/* mblock = "abcdabcdab" */
dest

Puntero al bloque de memoria.

size

Tamaño en bytes del bloque dest. No es necesario que sea múltiplo de 4.

mask

Máscara de 4 bytes.


bmem_set8 ()

Rellena un bloque de memoria con la misma máscara de 8-bytes.

void
bmem_set8(byte_t *dest,
          const uint32_t size,
          const byte_t *mask);
dest

Puntero al bloque de memoria.

size

Tamaño en bytes del bloque dest. No es necesario que sea múltiplo de 8.

mask

Máscara de 8 bytes.


bmem_set16 ()

Rellena un bloque de memoria con la misma máscara de 16-bytes.

void
bmem_set16(byte_t *dest,
           const uint32_t size,
           const byte_t *mask);
dest

Puntero al bloque de memoria.

size

Tamaño en bytes del bloque dest. No es necesario que sea múltiplo de 16.

mask

Máscara de 16 bytes.


bmem_set_u32 ()

Rellena un array de tipo uint32_t con el mismo valor.

void
bmem_set_u32(uint32_t *dest,
             const uint32_t n,
             const uint32_t value);
dest

Puntero al array.

n

Tamaño del array (número de elementos).

value

Valor de relleno.


bmem_is_zero ()

Comprueba si un bloque de memoria está completamente relleno de 0s.

bool_t
bmem_is_zero(const byte_t *mem,
             const uint32_t size);
mem

Puntero al bloque de memoria.

size

Tamaño en bytes del bloque mem.

Retorna

TRUE si todas las posiciones son 0, de lo contrario FALSE.


bmem_set_zero ()

Rellena un bloque de memoria con 0s.

void
bmem_set_zero(byte_t *dest,
              const uint32_t size);
dest

Puntero al bloque de memoria que debe ser rellenado.

size

Tamaño en bytes del bloque dest.


bmem_zero ()

Inicializa un objeto con 0s.

void
bmem_zero(type *dest,
          type);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
typedef struct
{
    uint32_t f1;
    real32_t f2;
    String *f3;
    ...
} MyType;

MyType t1;
bmem_zero(&t1, MyType);
/* t1 = {0} */
dest

Puntero al objeto.

type

Tipo de objeto.


bmem_zero_n ()

Inicializa un array de objetos con 0s.

void
bmem_zero_n(type *dest,
            const uint32_t n,
            type);
dest

Array de objetos.

n

Tamaño del array.

type

Tipo de objeto.


bmem_copy ()

Copia el contenido de un bloque en otro. Los bloques no deben estar superpuestos.

void
bmem_copy(byte_t *dest,
          const byte_t *src,
          const uint32_t size);
dest

Puntero al bloque de destino.

src

Puntero al bloque de origen.

size

Número de bytes a copiar.


bmem_copy_n ()

Copia un array de objetos en otra localización.

void
bmem_copy_n(type *dest,
            const type *src,
            const uint32_t n,
            type);
1
2
3
real32_t v1[64];
real32_t v2[64]; = {1.f, 45.f, 12.4f, ...};
bmem_copy_n(v1, v2, 64, real32_t);
dest

Puntero al array de destino.

src

Puntero al array de origen.

n

Tamaño del array (número de elementos, no bytes).

type

Tipo de objeto.


bmem_move ()

Igual que bmem_copy, pero los bloques pueden estar superpuestos.

void
bmem_move(byte_t *dest,
          const byte_t *src,
          const uint32_t size);
dest

Puntero al bloque de destino.

src

Puntero al bloque de origen.

size

Número de bytes a copiar.

Observaciones

Si tenemos la certeza de que ambos bloque no se solapan, bmem_copy es mucho más eficiente.


bmem_overlaps ()

Comprueba si dos bloques de memoria se solapan.

bool_t
bmem_overlaps(byte_t *mem1,
              byte_t *mem2,
              const uint32_t size1,
              const uint32_t size2);
mem1

Puntero al primer bloque.

mem2

Puntero al segundo bloque.

size1

Tamaño del primer bloque (en bytes).

size2

Tamaño del segundo bloque (en bytes).

Retorna

TRUE si hay solapamiento.


bmem_rev ()

Revierte un bloque de memoria m[i] = m[n-i-1].

void
bmem_rev(byte_t *mem,
         const uint32_t size);
mem

Puntero al bloque de memoria.

size

Tamaño del bloque en bytes.


bmem_rev2 ()

Revierte un bloque de memoria de 2-bytes.

void
bmem_rev2(byte_t *mem);
mem

Puntero al bloque de memoria.


bmem_rev4 ()

Revierte un bloque de memoria de 4-bytes.

void
bmem_rev4(byte_t *mem);
mem

Puntero al bloque de memoria.


bmem_rev8 ()

Revierte un bloque de memoria de 8-bytes.

void
bmem_rev8(byte_t *mem);
mem

Puntero al bloque de memoria.


bmem_revcopy ()

Realiza una copia revertida de un bloque de memoria.

void
bmem_revcopy(byte_t *dest,
             const byte_t *src,
             const uint32_t size);
dest

Puntero al bloque de destino.

src

Puntero al bloque de origen.

size

Número de bytes a copiar.


bmem_swap ()

Intercambia el contenido de dos bloques de memoria (no solapados). Al finalizar, mem1[i] = mem2[i] y mem2[i] = mem1[i].

void
bmem_swap(byte_t *mem1,
          byte_t *mem2,
          const uint32_t size);
mem1

Puntero al primer bloque.

mem2

Puntero al segundo bloque.

size

Número de bytes a intercambiar.


bmem_swap_type ()

Intercambia el contenido de dos objetos.

void
bmem_swap_type(type *obj1,
               type *obj2,
               type);
obj1

Primer objeto.

obj2

Segundo objeto.

type

Tipo de objeto.


bmem_shuffle ()

Desordena aleatoriamente (barajar) un bloque de memoria.

void
bmem_shuffle(byte_t *mem,
             const uint32_t size,
             const uint32_t esize);
mem

Puntero al bloque de memoria.

size

Tamaño del bloque (número de elementos).

esize

Tamaño de cada elemento.

Observaciones

Esta función se basa en un generador de números pseudo-aleatorios. Utiliza bmath_rand_seed para cambiar la secuencia.


bmem_shuffle_n ()

Desordena aleatoriamente (barajar) un array de objetos.

void
bmem_shuffle_n(type *array,
               const uint32_t size,
               type);
array

Array de elementos.

size

Número de elementos.

type

Tipo de objeto.

Observaciones

Esta función se basa en un generador de números pseudo-aleatorios. Utiliza bmath_rand_seed para cambiar la secuencia.

❮ Anterior
Siguiente ❯