Arrays
Muéstrame tus diagramas de flujo y oculta tus tablas y seguiré desconcertado. Muéstrame tus tablas y, generalmente, no necesitaré tus diagramas, serán obvios. La representación es la esencia de la programación. Frederick P. Brooks, Jr. - The Mythical Man Month.
Funciones
Poder trabajar con colecciones de datos es algo fundamental a la hora de diseñar nuestro modelo. Además de los tipos básicos y los struct
, union
o class
el lenguaje C nos ofrece la construcción array, que permite almacenar varios elementos bajo un mismo nombre de variable (Listado 1):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
O, de forma dinámica (Listado 2):
1 2 3 4 |
uint32_t n = get_n(); uint32_t *integers = heap_new_n(n, uint32_t); real32_t *reals = heap_new_n(n, real32_t); Product *products = heap_new_n(n, Product); |
Los arrays de C guardan los elementos en posiciones contiguas de memoria y, aunque son muy rápidos de consultar, carecen de funcionalidad para insertar, borrar, buscar u ordenar. En muchas ocasiones, los datos no están disponibles cuando se crea el contenedor, sino que se van entrando o saliendo de forma dinámica durante la ejecución del programa, por lo que no podemos prever de antemano un número máximo con el que realizar la reserva de memoria. El tipo Array
implementado en NAppGUI es, en esencia, un array de C dinámico más una serie de métodos para manipularlo. Por dinámico entendemos que la estructura ajusta su tamaño a la cantidad real de elementos, conservando la premisa principal de que todos permanezcan juntos en memoria.
Cuando se crea un Array
, se reserva memoria para unos pocos registros (Figura 1). Posteriormente, podremos añadir nuevos elementos al final (lo típico) o insertarlos en cualquier posición aleatoria en el caso de que ya tengamos datos en el contenedor. En este último caso, el resto de elementos serán desplazados hacia la derecha. En el momento que se supere la cantidad de registros reservados, se duplicará el bloque dinámico interno para dar cabida a las nuevas posiciones. De igual forma es posible eliminar cualquier elemento de la colección, desplazando el resto hacia la izquierda para mantener la coherencia espacial de la estructura. Si el número de items decreciese a la mitad, el bloque de memoria se reducirá. De esta forma, durante el tiempo de vida del contenedor, la memoria se irá ajustando multiplicando o dividiendo por 2 el número de elementos reservados.
1. Registros o punteros
Un objeto tipo Product
, nuestra estructura de ejemplo, ocupa 20 bytes en sistemas de 32bits (Figura 2). Los campos code
, description
e image64
son punteros que apuntan a otras zonas de memoria, donde residen los campos tipo String
e Image
reservados dinámicamente.
En función de lo que se almacene dentro del contenedor, podemos utilizar dos clases de array (Listado 3). Los array de registros guardarán la totalidad del objeto (los 20 bytes) y los array de punteros solo una referencia al mismo (4 bytes), estando el objeto real ubicado en otra dirección de memoria (Figura 3). A pesar que la gestión interna de la estructura es la misma, el acceso a los elementos difiere ligeramente.
- Utiliza arrst_create para crear un array de registros.
- Utiliza arrpt_create para crear un array de punteros.
1 2 |
ArrSt(Product) *arrst = arrst_create(Product); ArrPt(Product) *arrpt = arrpt_create(Product); |
Utilizar ArrSt puede mejorar ligeramente el rendimiento, gracias a la coherencia espacial, que reduce los fallos de caché, y al ahorro de llamadas al gestor de memoria Comparativa Arrays vs Sets. Pero esto no siempre será posible, y no podremos utilizarlos en estos casos:
- Objetos opacos: Si la definición del tipo no es pública, el contenedor no puede calcular el espacio necesario para cada elemento, por lo que solo podremos trabajar con punteros a los mismos.
- Objetos compartidos: Si otras estructuras del modelo mantienen punteros a los elementos del contenedor, tendremos problemas del tipo Segmentation Fault debido al cambio de las direcciones de memoria al reubicar el bloque interno del contenedor (Figura 4).
2. Comprobación de tipos
Habrás observado en (Listado 1) que aparecen dos sentencias justamente después de la definición del struct Product
: DeclSt y DeclPt. Son dos macros que habilitan la comprobación de tipos en tiempo de compilación, definiendo una interfaz personalizada en los contenedores para este nuevo tipo (Listado 4). Salvando las distancias, imitan las template<>
de C++. DeclSt
habilita los contenedores de registros y DeclPt
los de punteros.
1 2 3 |
Product *p1 = arrst_new(Product); Product *p2 = arrst_get(arrst, 5, Product); const Product *p3 = arrst_get_const(arrst, 5, Product); |
Aunque no es aconsejable, puedes prescindir del uso de estas macros y utilizar las interfaces "en crudo" de los contenedores, definidas en array.h
y rbtree.h
. En este caso tu código será mucho menos legible y no tendrás el soporte del compilador.
Las cabecerasarray.h
yrbtree.h
no están documentadas.
3. Constructores
Cuando se reserva memoria para un objeto, bien sea como variables automáticas en el Segmento Stack
1 |
Product product; |
en el Segmento Heap, mediante memoria dinámica
1 |
Product *product = heap_new(Product);
|
o en un contenedor
1 |
Product *product = arrst_new(array, Product);
|
el contenido inicial del mismo es basura, entendido como bytes indeterminados. Inicializar un objeto es asignar valores válidos y coherentes a cada campo del mismo (Listado 5).
Product
.
1 2 3 4 5 6 7 8 |
static void i_init(Product *product) { product->type = ekCPU; product->code = str_c(""); product->description = str_c(""); product->image64 = image_copy(gui_image(NOIMAGE_PNG)); product->price = 0.f; } |
Por su parte, un constructor es un inicializador que previamente reserva memoria de forma dinámica para almacenar el objeto (Listado 6).
Product
.
1 2 3 4 5 6 |
static Product *i_create(void) { Product *product = heap_new(Product); i_init(product); return product; } |
Cuando utilizamos arrays de registros, solo necesitaremos inicializar el objeto, ya que el espacio para almacenarlo ha sido reservado por el propio contenedor (Listado 7). Sin embargo, en arrays de punteros, la memoria para el objeto debe ser reservada explícitamente, ya que el contenedor solo guardará una referencia.
1 2 3 4 5 6 7 8 9 10 11 12 |
// Add an item using an automatic variable (a copy is required) Product product; i_init(&product); arrst_append(array, product, Product); // Add an item directly (avoiding copying) Product *product = arrst_new(array, Product); i_init(product); // Add a pointer to a newly created object on the heap Product *product = i_create(); arrpt_append(array, product, Product); |
Utilizar arrst_new, arrst_insert_n o arrst_prepend_n siempre que sea posible para insertar en arrays de registros, ya que evitan una copia del objeto.
4. Recorrido de un array
Para iterar sobre todos los elementos del array, podemos elegir entre dos tipos de sintaxis para implementar el bucle.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
uint32_t i, n = arrst_size(arrst, Product); for (i = 0; i < n; ++i) { const Product *product = arrst_get(arrst, i, Product); // Do something ... } arrst_foreach(product, arrst, Product) // Do something ... arrst_end() // In reverse order arrst_foreach_rev(product, arrst, Product) // Do something ... arrst_end() |
5. Copia de objetos
De forma similar a los constructores, existen dos métodos para copiar objetos (Listado 8). En el primero de ellos generamos memoria dinámica para los campos del objeto, pero no para el propio objeto bien por ser una variable automática o estar almacenado en un array de registros. En el segundo caso, reservamos memoria dinámica tanto para el objeto como para sus elementos.
Product
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static void i_copy_data(Product *dest, const Product *src) { dest->type = src->type; dest->code = str_copy(src->code); dest->description = str_copy(src->description); dest->image64 = image_copy(src->image64); dest->price = src->price; } static Product *i_copy(const Product *product) { Product *new_product = heap_new(Product); i_copy_data(new_product, product); return new_product; } ArrSt(Product) *arrst = arrst_copy(arrst_src, i_copy_data, Product); ArrPt(Product) *arrpt = arrpt_copy(arrpt_src, i_copy, Product); |
6. Serialización
Un caso especial de constructor son los readers (de-serializadores). Cuando creamos un array a partir del contenido de Streams (Listado 9), necesitamos un método capaz de crear o inicializar un elemento desde el propio stream. Dependiendo del tipo de contenedor será necesario reservar memoria para cada elemento o no.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static void i_read_data(Stream *stm, Product *product) { product->type = stm_read_enum(stm, type_t); product->code = str_read(stm); product->description = str_read(stm); product->image64 = image_read(stm); product->price = stm_read_r32(stm); } static Product *i_read(Stream *stream) { Product *product = heap_new(Product); i_read_data(stream, product); return product; } ArrSt(Product) *arrst = arrst_read(i_read_data, Product); ArrPt(Product) *arrpt = arrpt_read(i_read, Product); |
De igual forma podemos escribir (serializar) el contenido de un array en un stream de escritura (Listado 10). En este caso, una sola función de escritura es suficiente para ambos tipos de contenedores, ya que cada uno sabe como acceder a sus elementos.
1 2 3 4 5 6 7 8 9 10 11 |
static void i_write(Stream *stm, const Product *product) { stm_write_enum(stm, product->type, type_t); str_write(stm, product->code); str_write(stm, product->description); image_write(stm, product->image64); stm_write_r32(stm, product->price); } arrst_write(stm, arrst, i_write, Product); arrpt_write(stm, arrpt, i_write, Product); |
7. Destructores
En programación muchas veces nos crean confusión los verbos: 'delete', 'destroy', 'free', 'erase', 'remove', 'clear' ya que en esencia significan lo mismo pero con pequeños matices. En NAppGUI utilizaremos un verbo u otro en función de acciones concretas:
- Free: Solo libera la memoria dinámica asignada a un objeto (Listado 11). Necesita un doble puntero, ya que el objeto será invalidado (
=NULL
) tras liberarlo, evitando referencias a zonas de memoria liberadas. - Remove: Destruye los campos de un objeto, pero no libera la memoria del propio objeto. Es el opuesto al inicializador (Listado 12).
- Destroy: La combinación de las dos anteriores. Destruye los campos del objeto y libera su memoria (Listado 13). Es el opuesto al constructor. Obviamente, requiere un doble puntero para invalidar la referencia.
- Delete: Borra un elemento de un array u otro tipo de contenedor (Listado 14). Puede tener asociado un destructor o 'remover', aunque no es obligatorio.
- Clear: Borra todos los elementos de un contenedor, pero no lo destruye, solo lo deja a cero (Listado 15). Al igual que arrst_delete, opcionalmente puede liberar memoria de los elementos.
1 2 3 4 |
1 2 3 4 5 6 7 8 |
static void i_remove(Product *product) { str_destroy(&product->code); str_destroy(&product->description); image_destroy(&product->image64); } arrst_destroy(&arrst, i_remove, Product); |
1 2 3 4 5 6 7 |
static void i_destroy(Product **product) { i_remove(*product); heap_free(product, Product); } arrpt_destroy(&arrpt, i_destroy, Product); |
1 2 3 4 5 6 7 8 |
// Just delete. arrst_delete(arrst, 4, NULL, Product); // Delete and remove (arrst). arrst_delete(arrst, 4, i_remove, Product); // Delete and destroy (arrpt). arrpt_delete(arrpt, 4, i_destroy, Product); |
1 2 3 4 5 6 7 8 |
// Just delete all. arrst_clear(arrst, NULL, Product); // Delete and remove all (arrst). arrst_clear(arrst, i_remove, Product); // Delete and destroy all (arrpt). arrpt_clear(arrpt, i_destroy, Product); |
8. Ordenar y buscar
La forma habitual de utilizar arrays será ir añadiendo elementos al final mediante arrst_new o arrpt_append para después iterar sobre el conjunto. Este orden "natural" será suficiente en la mayoría de casos, pero es posible que necesitemos organizar los elementos siguiendo otro criterio para:
- Presentar la información ordenada por uno o varios campos de la estructura.
- Optimizar búsquedas. Para localizar un determinado elemento, no hay más remedio que recorrer todo el array, con coste lineal
O(n)
. Pero podemos resolver la búsqueda en tiempo logarítmicoO(logn)
si el array está ordenado, incrementando drásticamente el rendimiento especialmente en conjuntos grandes (Figura 5). - Utiliza la función arrst_sort, para ordenar un array. Tendremos que pasar una función de comparación, que es la que determinará la relación de orden (Listado 16).
1 2 3 4 5 6 7 |
static int i_compare(const Product *p1, const Product *p2) { return str_scmp(p1->code, p2->code); } arrst_sort(arrst, i_compare, Product); arrpt_sort(arrpt, i_compare, Product); |
Para buscar un elemento dentro de un array, también deberemos proporcionar una función que compare el objeto con una clave. Esta clave contiene el criterio de búsqueda y, normalmente, es más reducida que el elemento en sí. Muchas veces no es más que un simple número o una cadena de texto (Listado 17).
- arrst_search Método lento. Buscará elementos de forma lineal, uno a uno
O(n)
. - arrst_bsearch Método rápido. Buscará elementos de forma logarítmica,
O(logn)
. El array debe estar ordenado bajo el mismo criterio que la búsqueda.
1 2 3 4 5 6 7 8 9 10 11 12 |
static int i_compare_key(const Product *p1, const char_t *key) { return str_cmp(p1->code, key); } uint32_t pos; Product *pr1, *pr2; // Slow O(n) pr1 = arrst_search(arrst, i_compare_key, "G3900", &pos, Product, char_t); // Fast O(logn) pr2 = arrst_bsearch(arrst, i_compare_key, "G3900", &pos, Product, char_t); |
9. Arrays de tipos básicos
Los tipos básicos son un caso particular de estructura de un solo campo, por lo utilizaremos ArrSt. En el caso concreto de enum
deberemos crear un alias mediante typedef
, ya que ArrSt(type)
no admite el keyword enum
, de igual forma que tampoco admite el keyword struct
. En C++ este alias no es necesario. Al destruir el array pasaremos NULL
al parámetro destructor, ya que los tipos básicos no generan memoria dinámica.
1 2 3 4 5 |
typedef enum _type_t type_t; ArrSt(uint32_t) *integers = arrst_create(uint32_t); ArrSt(type_t) *types = arrst_create(type_t); arrst_destroy(&integers, NULL, uint32_t); arrst_destroy(&types, NULL, type_t); |
arrst_create ()
Crea un array vacío.
ArrSt(type)* arrst_create(type);
type | Tipo de objeto. |
Retorna
El nuevo array.
arrst_copy ()
Crea una copia de un array.
ArrSt(type)* arrst_copy(const ArrSt(type) *array, FPtr_scopy func_copy, type);
array | El array original. |
func_copy | Función que debe copiar los campos de cada objeto. |
type | Tipo de objeto. |
Retorna
La copia del array original.
Observaciones
La función de copia debe asignar memoria a los campos que lo requieran, pero NO al objeto en sí mismo. Si pasamos NULL
, se realizará una copia byte a byte del objeto original, lo que puede suponer un riesgo de integridad si los elementos del array contienen objetos String
o de otro tipo que necesiten memoria dinámica. Ver Copia de objetos.
arrst_read ()
Crea un array leyendo su contenido de un Streams (de-serialización).
ArrSt(type)* arrst_read(Stream *stream, FPtr_read_init func_read, type);
stream | Un stream de lectura. |
func_read | Función para inicializar un objeto a partir de los datos obtenidos de un stream. Esta función no debe reservar memoria para el propio objeto (el contenedor ya lo hace). Serialización. |
type | Tipo de objeto. |
Retorna
El array leído.
arrst_destroy ()
Destruye un array y todos sus elementos.
void arrst_destroy(ArrSt(type) **array, FPtr_remove func_remove, type);
array | El array. Será puesto a |
func_remove | Función que debe liberar la memoria asociada a los campos del objeto, pero no el objeto en si mismo Destructores. Si es |
type | Tipo de objeto. |
arrst_destopt ()
Destruye un array y todos sus elementos, siempre y cuando el objeto array no sea NULL
.
void arrst_destopt(ArrSt(type) **array, FPtr_remove func_remove, type);
array | El array. |
func_remove | Ver arrst_destroy. |
type | Tipo de objeto. |
arrst_clear ()
Borra el contenido del array, sin destruir el contenedor que quedará con cero elementos.
void arrst_clear(ArrSt(type) *array, FPtr_remove func_remove, type);
array | El array. |
func_remove | Función 'remove'. Ver arrst_destroy. |
type | Tipo de objeto. |
arrst_write ()
Escribe un array en un Streams (serialización).
void arrst_write(Stream *stream, const ArrSt(type) *array, FPtr_write func_write, type);
stream | Un stream de escritura. |
array | El array. |
func_write | Función que escribe el contenido de un elemento en un stream Serialización. |
type | Tipo de objeto. |
arrst_size ()
Obtiene el número de elementos en un array.
uint32_t arrst_size(const ArrSt(type) *array, type);
array | El array. |
type | Tipo de objeto. |
Retorna
Número de elementos.
arrst_get ()
Obtiene un puntero al elemento en la posición pos
.
type* arrst_get(ArrSt(type) *array, const uint32_t pos, type);
array | El array. |
pos | Posición o índice del elemento. |
type | Tipo de objeto. |
Retorna
Puntero al elemento.
arrst_get_const ()
Obtiene un puntero const al elemento en la posición pos
.
const type* arrst_get_const(const ArrSt(type) *array, const uint32_t pos, type);
array | El array. |
pos | Posición o índice del elemento. |
type | Tipo de objeto. |
Retorna
Puntero al elemento.
arrst_first ()
Obtiene un puntero al primer elemento del array.
type* arrst_first(ArrSt(type) *array, type);
array | El array. |
type | Tipo de objeto. |
Retorna
Puntero al elemento.
arrst_first_const ()
Obtiene un puntero const al primer elemento del array.
const type* arrst_first_const(const ArrSt(type) *array, type);
array | El array. |
type | Tipo de objeto. |
Retorna
Puntero al elemento.
arrst_last ()
Obtiene un puntero al último elemento del array.
type* arrst_last(ArrSt(type) *array, type);
array | El array. |
type | Tipo de objeto. |
Retorna
Puntero al elemento.
arrst_last_const ()
Obtiene un puntero const al último elemento del array.
const type* arrst_last_const(const ArrSt(type) *array, type);
array | El array. |
type | Tipo de objeto. |
Retorna
Puntero al elemento.
arrst_all ()
Obtiene un puntero a la memoria interna del array, que da acceso directo a todos los elementos.
type* arrst_all(ArrSt(type) *array, type);
array | El array. |
type | Tipo de objeto. |
Retorna
Puntero base. Incrementándolo uno a uno iteraremos sobre los elementos.
Observaciones
Utiliza arrst_foreach para iterar sobre los elementos de forma más segura y elegante.
arrst_all_const ()
Obtiene un puntero const a la memoria interna del array, que da acceso directo a todos los elementos.
const type* arrst_all_const(const ArrSt(type) *array, type);
array | El array. |
type | Tipo de objeto. |
Retorna
Puntero base. Incrementándolo uno a uno iteraremos sobre los elementos.
Observaciones
Utiliza arrst_foreach_const para iterar sobre los elementos de forma más segura y elegante.
arrst_grow ()
Añade n
elementos, sin inicializar, al final del array.
void arrst_grow(ArrSt(type) *array, const uint32_t n, type);
array | El array. |
n | Cantidad de elementos a añadir. |
type | Tipo de objeto. |
arrst_new ()
Reserva espacio para un elemento al final del array.
type* arrst_new(ArrSt(type) *array, type);
1 2 3 4 5 6 7 8 |
// arrst_append copies 'product' Product product; i_init_product(&product, ...); arrst_append(array, product, Product); // arrst_new avoids the copy Product *product = arrst_new(array, Product); i_init_product(product, ...); |
array | El array. |
type | Tipo de objeto. |
Retorna
Puntero al elemento añadido.
Observaciones
Es ligeramente más rápido que arrst_append
, especialmente en estructuras grandes, ya que evita copiar el contenido del objeto. El contenido inicial de la memoria es indeterminado.
arrst_new0 ()
Reserva espacio para un elemento al final del array y lo inicializa a 0.
type* arrst_new0(ArrSt(type) *array, type);
array | El array. |
type | Tipo de objeto. |
Retorna
Puntero al elemento añadido.
Observaciones
Igual que arrst_new pero inicializando toda la memoria a 0.
arrst_new_n ()
Reserva espacio para varios elementos al final del array.
type* arrst_new_n(ArrSt(type) *array, const uint32_t n, type);
array | El array. |
n | Número de elementos a añadir. |
type | Tipo de objeto. |
Retorna
Puntero al primer elemento añadido.
Observaciones
Igual que arrst_new pero reservando varios elementos en la misma llamada. El contenido inicial de la memoria es indeterminado.
arrst_new_n0 ()
Reserva espacio para varios elementos al final del array y los inicializa a 0.
type* arrst_new_n0(ArrSt(type) *array, const uint32_t n, type);
array | El array. |
n | Número de elementos a añadir. |
type | Tipo de objeto. |
Retorna
Puntero al primer elemento añadido.
Observaciones
Igual que arrst_new_n pero pero inicializando toda la memoria a 0.
arrst_prepend_n ()
Reserva espacio para varios elemento al principio del array. El resto de elementos serán desplazados a la derecha.
type* arrst_prepend_n(ArrSt(type) *array, const uint32_t n, type);
array | El array. |
n | Número de elementos a insertar. |
type | Tipo de objeto. |
Retorna
Puntero al primer elemento insertado.
Observaciones
El contenido inicial de la memoria es indeterminado.
arrst_insert_n ()
Reserva espacio para varios elementos en una posición arbitraria del array.
type* arrst_insert_n(ArrSt(type) *array, const uint32_t pos, const uint32_t n, type);
array | El array. |
pos | Posición donde será insertado. El actual elemento en |
n | Número de elementos a insertar. |
type | Tipo de objeto. |
Retorna
Puntero al primer elemento insertado.
Observaciones
El contenido inicial de la memoria es indeterminado.
arrst_append ()
Añade un elemento al final del array.
void arrst_append(ArrSt(type) *array, type value, type);
array | El array. |
value | Elemento a añadir. |
type | Tipo de objeto. |
arrst_prepend ()
Inserta un elemento al inicio del array. El resto de elementos serán desplazados a la derecha.
void arrst_prepend(ArrSt(type) *array, type value, type);
array | El array. |
value | Elemento a insertar. |
type | Tipo de objeto. |
arrst_insert ()
Inserta un elemento en una posición arbitraria del array.
void arrst_insert(ArrSt(type) *array, const uint32_t pos, type value, type);
array | El array. |
pos | Posición donde será insertado. El actual elemento en |
value | Elemento a insertar. |
type | Tipo de objeto. |
arrst_join ()
Une dos vectores. Añade todos los elementos de src
al final de dest
.
void arrst_join(ArrSt(type) *dest, const ArrSt(type) *src, FPtr_scopy func_copy, type);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
ArrSt(Product) *products = create_products(...); ArrSt(Product) *new_products = new_products(...); // Join without 'copy' func. Dynamic 'Product' fields will be reused. arrst_join(products, new_products, NULL, Product); arrst_destroy(&new_products, NULL, Product); ... arrst_destroy(&products, i_remove, Product); // Join with 'copy' func. Dynamic 'Product' fields will be duplicate. arrst_join(products, new_products, i_copy_data, Product); arrst_destroy(&new_products, i_remove, Product); ... arrst_destroy(&products, i_remove, Product); |
dest | El array destino. |
src | El array cuyos elementos serán añadidos a |
func_copy | Función de copia del objeto. |
type | Tipo de objeto. |
Observaciones
La función de copia debe crear memoria dinámica para los campos que lo requieran, pero NO para el objeto en sí mismo. Ver arrst_copy. Si es NULL
se realizará una copia byte a byte del elemento.
arrst_delete ()
Elimina un elemento del array.
void arrst_delete(ArrSt(type) *array, const uint32_t pos, FPtr_remove func_remove, type);
array | El array. |
pos | Posición del elemento a borrar. El actual elemento en |
func_remove | Función 'remove'. Ver arrst_destroy. |
type | Tipo de objeto. |
arrst_pop ()
Elimina el último elemento del array.
void arrst_pop(ArrSt(type) *array, FPtr_remove func_remove, type);
array | El array. |
func_remove | Función 'remove'. Ver arrst_destroy. |
type | Tipo de objeto. |
arrst_sort ()
Ordena los elementos del array utilizando Quicksort.
void arrst_sort(ArrSt(type) *array, FPtr_compare func_compare, type);
array | El array. |
func_compare | Función para comparar dos elementos. Ordenar y buscar. |
type | Tipo de objeto. |
arrst_sort_ex ()
Ordena los elementos del array utilizando Quicksort y datos adicionales.
void arrst_sort_ex(ArrSt(type) *array, FPtr_compare_ex func_compare, type, dtype);
array | El array. |
func_compare | Función para comparar dos elementos utilizando un dato adicional. |
type | Tipo de objeto. |
dtype | Tipo de dato en la función de comparación. |
arrst_search ()
Busca un elemento en el array de forma lineal O(n)
.
type* arrst_search(ArrSt(type) *array, FPtr_compare func_compare, const ktype *key, uint32_t *pos, type, ktype);
1 2 3 |
uint32_t pos; uint32_t key = 345; Product *product = arrst_search(arrst, i_compare_key, &key, &pos, Product, uint32_t); |
array | El array. |
func_compare | Función de comparación. El primer parámetro es el elemento, el segundo la clave de búsqueda. Ordenar y buscar. |
key | Clave a buscar. Puntero a un tipo de dato que puede ser diferente al tipo de elemento del array. |
pos | Posición del elemento en el array (si existe), o |
type | Tipo de objeto. |
ktype | Tipo de clave. |
Retorna
Puntero al primer elemento que coincida con el criterio de búsqueda o NULL
si no existe ninguno.
arrst_search_const ()
Versión const de arrst_search.
const type* arrst_search_const(const ArrSt(type) *array, FPtr_compare func_compare, const ktype *key, uint32_t *pos, type, ktype);
array | El array. |
func_compare | Función de comparación. |
key | Clave a buscar. Puntero a un tipo de dato que puede ser diferente al tipo de elemento del array. |
pos | Posición del elemento en el array. |
type | Tipo de objeto. |
ktype | Tipo de clave. |
Retorna
Puntero al elemento.
arrst_bsearch ()
Busca un elemento en el array de forma logarítmica O(logn)
.
type* arrst_bsearch(ArrSt(type) *array, FPtr_compare func_compare, const ktype *key, uint32_t *pos, type, ktype);
array | El array. |
func_compare | Función de comparación. El primer parámetro es el elemento, el segundo la clave de búsqueda. Ordenar y buscar. |
key | Clave a buscar. Puntero a un tipo de dato que puede ser diferente al tipo de elemento del array. |
pos | Posición del elemento en el array (si existe), o |
type | Tipo de objeto. |
ktype | Tipo de clave. |
Retorna
Puntero al primer elemento que coincida con el criterio de búsqueda o NULL
si no existe ninguno.
Observaciones
El array debe estar ordenado según el mismo criterio que la búsqueda. De no ser así el resultado es impredecible.
arrst_bsearch_const ()
Versión const de arrst_bsearch.
const type* arrst_bsearch_const(const ArrSt(type) *array, FPtr_compare func_compare, const ktype *key, uint32_t *pos, type, ktype);
array | El array. |
func_compare | Función de comparación. |
key | Clave a buscar. |
pos | Posición del elemento en el array. |
type | Tipo de objeto. |
ktype | Tipo de clave. |
Retorna
Puntero al elemento.
arrst_foreach ()
Itera sobre todos los elementos del array. Usa arrst_end
para cerrar el bucle.
void arrst_foreach(type *elem, ArrSt(type) *array, type);
1 2 3 |
arrst_foreach(product, array, Product) bstd_printf("Index:%d, Id:%d\n", product_i, product->id); arrst_end() |
elem | Nombre de la variable 'elemento' dentro del bucle. Añadiendo el sufijo |
array | El array. |
type | Tipo de objeto. |
arrst_foreach_const ()
Versión const de arrst_foreach.
void arrst_foreach_const(const type *elem, const ArrSt(type) *array, type);
elem | Elemento. |
array | El array. |
type | Tipo de objeto. |
arrst_forback ()
Itera sobre todos los elementos del array hacia atrás, desde el último al primero. Usa arrst_end
para cerrar el bucle.
void arrst_forback(type *elem, ArrSt(type) *array, type);
1 2 3 4 |
// Now in reverse order arrst_forback(product, array, Product) bstd_printf("Index:%d, Id:%d\n", product_i, product->id); arrst_end() |
elem | Nombre de la variable 'elemento' dentro del bucle. Añadiendo el sufijo |
array | El array. |
type | Tipo de objeto. |
arrst_forback_const ()
Versión const de arrst_forback.
void arrst_forback_const(const type *elem, const ArrSt(type) *array, type);
elem | Elemento. |
array | El array. |
type | Tipo de objeto. |
arrst_end ()
Cierra el bucle abierto por arrst_foreach, arrst_foreach_const, arrst_forback o arrst_forback_const.
void
arrst_end(void);