SDK Multiplataforma en C logo

SDK Multiplataforma en C

Arrays

❮ Anterior
Siguiente ❯

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

ArrSt(type)*arrst_create (...)
ArrSt(type)*arrst_copy (...)
ArrSt(type)*arrst_read (...)
voidarrst_destroy (...)
voidarrst_destopt (...)
voidarrst_clear (...)
voidarrst_write (...)
uint32_tarrst_size (...)
type*arrst_get (...)
type*arrst_first (...)
type*arrst_last (...)
type*arrst_all (...)
voidarrst_grow (...)
type*arrst_new (...)
type*arrst_new0 (...)
type*arrst_new_n (...)
type*arrst_new_n0 (...)
type*arrst_prepend_n (...)
type*arrst_insert_n (...)
voidarrst_append (...)
voidarrst_prepend (...)
voidarrst_insert (...)
voidarrst_join (...)
voidarrst_delete (...)
voidarrst_pop (...)
voidarrst_sort (...)
voidarrst_sort_ex (...)
type*arrst_search (...)
type*arrst_bsearch (...)
voidarrst_foreach (...)
voidarrst_foreach_rev (...)
voidarrst_end (void)

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):

Listado 1: Arrays en C.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
typedef struct _product_t Product;
struct _product_t
{
    type_t type;
    String *code;
    String *description;
    Image *image64;
    real32_t price;
};

uint32_t integers[100];
real32_t reals[100];
Product products[100];

O, de forma dinámica (Listado 2):

Listado 2: Arrays dinámicos.
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.

Ajuste de una estructura array al número de elementos que tiene almacenados.
Figura 1: Los Array adaptarán su memoria interna al número de elementos.

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.

Bytes en memoria representando un objeto dinámico.
Figura 2: Objeto Product.

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.

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).
  • Error de violación de segmento al rehubicar el bloque de memoria que almacena el array.
    Figura 4: Los arrays de registros son peligrosos con referencias externas.

2. 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 4).

Listado 4: Inicializando un objeto 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 5).

Listado 5: Constructor de un objeto 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 6). Sin embargo, en arrays de punteros, la memoria para el objeto debe ser reservada explícitamente, ya que el contenedor solo guardará una referencia.

Listado 6: Insertar objetos correctamente inicializados.
 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.

3. 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();

4. Copia de objetos

De forma similar a los constructores, existen dos métodos para copiar objetos (Listado 7). 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.

Listado 7: Copiando un objeto 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);

5. Serialización

Un caso especial de constructor son los readers (de-serializadores). Cuando creamos un array a partir del contenido de Streams (Listado 8), 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.

Listado 8: Leyendo un array desde un stream.
 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 9). 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.

Listado 9: Escribiendo un array en un stream.
 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);

6. 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 10). Necesita un doble puntero, ya que el objeto será invalidado (=NULL) tras liberarlo, evitando referencias a zonas de memoria liberadas.
  • Listado 10: Liberando la memoria de un objeto.
    1
    2
    3
    4
    
    Product *product = heap_new(Product);
    ...
    heap_free(&product, Product);
    // product = NULL
    
  • Remove: Destruye los campos de un objeto, pero no libera la memoria del propio objeto. Es el opuesto al inicializador (Listado 11).
  • Listado 11: Liberando la memoria de los campos del objeto.
    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);
    
  • Destroy: La combinación de las dos anteriores. Destruye los campos del objeto y libera su memoria (Listado 12). Es el opuesto al constructor. Obviamente, requiere un doble puntero para invalidar la referencia.
  • Listado 12: Libera la memoria del objeto y todo su contenido.
    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);
    
  • Delete: Borra un elemento de un array u otro tipo de contenedor (Listado 13). Puede tener asociado un destructor o 'remover', aunque no es obligatorio.
  • Listado 13: Elimina un elemento de un contenedor.
    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);
    
  • Clear: Borra todos los elementos de un contenedor, pero no lo destruye, solo lo deja a cero (Listado 14). Al igual que arrst_delete, opcionalmente puede liberar memoria de los elementos.
  • Listado 14: Elimina todos los elementos de un contenedor.
    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);
    

7. 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ítmico O(logn) si el array está ordenado, incrementando drásticamente el rendimiento especialmente en conjuntos grandes (Figura 5).
  • Ilustración de una búsqueda dicotómica en un conjunto de 1000 elementos.
    Figura 5: En un máximo de 10 pasos encontraremos un elemento entre mil (20 pasos para un millón).
  • 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 15).
  • Listado 15: Ordenar arrays por código de producto.
    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 16).

  • 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.
  • Listado 16: Búsqueda de un elemento por su código.
     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);
    

8. 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 NULL tras la destrucción.

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 NULL se liberará solo el array y no el contenido interno de los elementos.

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(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(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(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(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 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 elemento 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 pos y siguientes serán desplazados a la derecha.

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 pos y siguientes serán desplazados a la derecha.

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

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 pos+1 y siguientes serán desplazados a la izquierda.

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,
             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 UINT32_MAX si no existe. Puede ser NULL.

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_bsearch ()

Busca un elemento en el array de forma logarítmica O(logn).

type*
arrst_bsearch(ArrSt(type) *array,
              FPtr_compare func_compare,
              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 UINT32_MAX si no existe. Puede ser NULL.

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_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 '_i' obtenemos el índice.

array

El array.

type

Tipo de objeto.


arrst_foreach_rev ()

Itera sobre todos los elementos del array hacia atrás, desde el último al primero. Usa arrst_end para cerrar el bucle.

void
arrst_foreach_rev(type *elem,
                  ArrSt(type) *array,
                  type);
1
2
3
4
// Now in reverse order
arrst_foreach_rev(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 '_i' obtenemos el índice.

array

El array.

type

Tipo de objeto.


arrst_end ()

Cierra el bucle abierto por arrst_foreach o arrst_foreach_rev.

void
arrst_end(void);
❮ Anterior
Siguiente ❯