Memoria
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 (...) |
void | bmem_free (...) |
void | bmem_set1 (...) |
void | bmem_set4 (...) |
void | bmem_set8 (...) |
void | bmem_set16 (...) |
void | bmem_set_u32 (...) |
void | bmem_set_r32 (...) |
int | bmem_cmp (...) |
bool_t | bmem_is_zero (...) |
void | bmem_set_zero (...) |
void | bmem_zero (...) |
void | bmem_zero_n (...) |
void | bmem_copy (...) |
void | bmem_copy_n (...) |
void | bmem_move (...) |
bool_t | bmem_overlaps (...) |
void | bmem_rev (...) |
void | bmem_rev2 (...) |
void | bmem_rev4 (...) |
void | bmem_rev8 (...) |
void | bmem_revcopy (...) |
void | bmem_rev_elems (...) |
void | bmem_swap (...) |
void | bmem_swap_type (...) |
void | bmem_shuffle (...) |
void | bmem_shuffle_n (...) |
Desde la perspectiva 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.
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.
NAppGUI implementa su propio gestor/auditor de memoria dinámica Heap 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 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 |
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 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 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 |
new_size | Nuevo tamaño requerido, en bytes. |
align | Alineación. Debe ser potencia de 2. |
Retorna
Puntero al bloque realojado.
Observaciones
Utiliza Heap 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 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 |
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 |
dest | Puntero al bloque de memoria. |
size | Tamaño en bytes del bloque |
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 |
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 |
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_set_r32 ()
Rellena un array de tipo real32_t con el mismo valor.
void bmem_set_r32(real32_t *dest, const uint32_t n, const real32_t value);
dest | Puntero al array. |
n | Tamaño del array (número de elementos). |
value | Valor de relleno. |
bmem_cmp ()
Compara dos bloques de memoria genéricos.
int bmem_cmp(const byte_t *mem1, const byte_t *mem2, const uint32_t size);
mem1 | Puntero al primer bloque de memoria. |
mem2 | Puntero al segundo bloque de memoria. |
size | Número de bytes a comparar. |
Retorna
Resultado de la comparación.
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 |
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 |
bmem_zero ()
Inicializa un objeto con 0s.
void bmem_zero(type *dest, type);
1 2 3 4 5 6 7 8 9 10 11 |
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_rev_elems ()
Revierte los elementos dentro de un array.
void bmem_rev_elems(type*, const uint32_t num_elems, type);
type* | Puntero al inicio del array. |
num_elems | Número de elementos del array. |
type | Tipo de objeto. |
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.