2013-12-22 3 views
4

Я просто написал красивую библиотеку, которая хорошо красит динамический массив, выделенный на куче в C. Он поддерживает множество операций, относительно просто использовать «чувство» почти как обычный старый добрый массив. Также легко смоделировать многие структуры данных на основе (стеки, очереди, кучи и т. Д.)Создание динамического массива C generic

Он может обрабатывать массивы любого типа, но проблема в том, что для каждой компиляции существует только один тип. C не имеет шаблонов, поэтому невозможно иметь, например, динамические массивы int и динамических массивов символов в одной и той же программе, что является проблемой.

Я не нашел никакого реального решения, все, что я нашел, связано с void *, и НЕТ, я НЕ хочу массив указателей пустоты. Приятно иметь возможность указывать указатели, но я также хочу иметь массив необработанных типов данных. (Таким образом, что вы можете добавить, например, 3, и доступ к нему нравится: array.data [я]

Должен ли я:

  1. Копировать/вставить в библиотеку один раз для каждого типа я хочу использовать (ужасно, но это будет работать и быть эффективным)

  2. Сделать гигантский макрос, который я могу расширить с помощью типа, который я хочу (так что он будет иметь тот же эффект, что и 1, но будет несколько элегантным и удобным)

  3. Укажите размер заостренных элементов как переменную, которая является частью o f динамическая структура массива. Будет работать в основном, но будет проблема с функциями, принимающими и возвращающими тип динамического массива напрямую. пустота * не всегда будет жизнеспособным вариантом

  4. Покиньте идею и использовать C++ вместо всякий раз, когда мне нужно такие дополнительные функции

Библиотека работает следующим образом: Использование

/* Variable length array library for C language 
* Usage : 
* Declare a variable length array like this : 
* 
* da my_array; 
* 
* Always initialize like this : 
* 
* da_init(&da);    // Creates a clean empty array 
* 
* Set a length to an array : 
* 
* da_setlength(&da, n);  // Note : if elements are added they'll be uninitialized 
*        // If elements are removed, they're permanently lost 
* 
* Always free memory before it goes out of scope (avoid mem leaks !) 
* 
* da_destroy(&da); 
* 
* Access elements much like a normal array : 
* - No boundary checks :   da.data[i] 
* - With boundary checks (debug) : da_get(data, i) 
* 
* da.length; // Return the current length of the variable length array (do NOT set the length by affecting this !! Use da_setlength instead.) 
* 
* You can add single elements at the end and beginning of array with 
* 
* da_add(&da, value);  // Add at the end 
* da_push(&da, value);  // Add at the front 
* 
* Retrieve values at the end and front of array (while removing them) with 
* 
* da_remove(&da);   // From the end 
* da_pop(&da);    // From the front 
* 
* Concatenate it with a standard array or another variable length array of same type with 
* 
* da_append(&da, array, array_length); // Standard array 
* da_append(&da, &db);     // Another variable length array 
*/ 

Реализация (Мне жаль, что это огромно, но я должен дать ему полноту вопроса)

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 

// Increment by which blocks are reserved on the heap 
#define ALLOC_BLOCK_SIZE 16 

// The type that the variable length array will contain. In this case it's "int", but it can be anything really (including pointers, arrays, structs, etc...) 
typedef int da_type; 

// Commend this to disable all kinds of bounds and security checks (once you're sure your program is fully tested, gains efficiency) 
#define DEBUG_RUNTIME_CHECK_BOUNDS 

// Data structure for variable length array variables 
typedef struct 
{ 
    da_type *start; // Points to start of memory allocated region 
    da_type *data;  // Points to logical start of array 
    da_type *end;  // Points to end of memory allocated region 
    size_t length;  // Length of the array 
} 
da; 

// Initialize variable length array, allocate 2 blocks and put start pointer at the beginning 
void da_init(da *da) 
{ 
    da_type *ptr = malloc(ALLOC_BLOCK_SIZE * sizeof(da_type)); 
    if(ptr == 0) exit(1); 
    da->start = ptr; 
    da->data = ptr; 
    da->end = da->start + ALLOC_BLOCK_SIZE; 
    da->length = 0; 
} 

// Set the da size directly 
void da_setlength(da *da, size_t newsize) 
{ 
    if(newsize % ALLOC_BLOCK_SIZE != 0) 
     newsize += ALLOC_BLOCK_SIZE - newsize % ALLOC_BLOCK_SIZE; 

    ptrdiff_t offs = da->data - da->start; 
    da_type *ptr = realloc(da->start, newsize * sizeof(da_type)); 
    if(!ptr) exit(1); 

    da->start = ptr; 
    da->data = ptr + offs; 
    da->end = ptr + newsize; 
    da->length = newsize; 
} 

// Destroy the variable length array (basically just frees memory) 
void da_destroy(da* da) 
{ 
    free(da->start); 
#ifdef DEBUG_RUNTIME_CHECK_BOUNDS 
    da->start = NULL; 
    da->data = NULL; 
    da->end = NULL; 
    da->length = 0; 
#endif 
} 

// Get an element of the array with it's index 
#ifdef DEBUG_RUNTIME_CHECK_BOUNDS 
    //Get an element of the array with bounds checking 
    da_type da_get(da *da, unsigned int index) 
    { 
     if(index >= da->length) 
     { 
      printf("da error : index %u is out of bounds\n", index); 
      exit(1); 
     } 
     return da->data[index]; 
    } 

    //Set an element of the array with bounds checking 
    void da_set(da *da, unsigned int index, da_type data) 
    { 
     if(index >= da->length) 
     { 
      printf("da error : index %u is out of bounds\n", index); 
      exit(1); 
     } 
     da->data[index] = data; 
    } 
#else 
    //Get an element of the array without bounds checking 
    #define da_get(da, index) ((da)->data[(index)]) 

    //Set an element of the array without bounds checking 
    #define da_set(da, index, v) (da_get(da, index) = v) 
#endif 


// Add an element at the end of the array 
void da_add(da *da, da_type i) 
{ // If no more memory 
    if(da->data + da->length >= da->end) 
    { // Increase size of allocated memory block 
     ptrdiff_t offset = da->data - da->start; 
     ptrdiff_t newsize = da->end - da->start + ALLOC_BLOCK_SIZE; 
     da_type *ptr = realloc(da->start, newsize * sizeof(da_type)); 
     if(!ptr) exit(1); 

     da->data = ptr + offset; 
     da->end = ptr + newsize; 
     da->start = ptr; 
    } 
    da->data[da->length] = i; 
    da->length += 1; 
} 

// Remove element at the end of the array (and returns it) 
da_type da_remove(da *da) 
{ 
#ifdef DEBUG_RUNTIME_CHECK_BOUNDS 
    if(da->length == 0) 
    { 
     printf("Error - try to remove item from empty array"); 
     exit(1); 
    } 
#endif 
    //Read last element of the array 
    da->length -= 1; 
    da_type ret_value = da->data[da->length]; 
    //Remove redundant memory if there is too much of it 
    if(da->end - (da->data + da->length) > ALLOC_BLOCK_SIZE) 
    { 
     ptrdiff_t offset = da->data - da->start; 
     ptrdiff_t newsize = da->end - da->start - ALLOC_BLOCK_SIZE; 
     da_type *ptr = realloc(da->start, newsize * sizeof(da_type)); 
     if(!ptr) exit(1); 

     da->data = ptr + offset; 
     da->end = ptr + newsize; 
     da->start = ptr; 
    } 
    return ret_value; 
} 

// Add element at the start of array 
void da_push(da *da, da_type i) 
{ //If array reaches bottom of the allocated space, we need to allocate more 
    if(da->data == da->start) 
    { 
     ptrdiff_t newsize = da->end - da->start + ALLOC_BLOCK_SIZE; 
     da_type *ptr = realloc(da->start, newsize * sizeof(da_type)); 
     if(!ptr) exit(1); 
     memmove(ptr + ALLOC_BLOCK_SIZE, ptr, da->length * sizeof(da_type)); 

     da->data = ptr + ALLOC_BLOCK_SIZE; 
     da->start = ptr; 
     da->end = ptr + newsize; 
    } 
    // Store element at start of array 
    da->length += 1; 
    da->data -= 1; 
    da->data[0] = i; 
} 

//Remove 1st element of array (and return it) 
da_type da_pop(da *da) 
{ 
#ifdef DEBUG_RUNTIME_CHECK_BOUNDS 
    if(da->length == 0) 
    { 
     printf("Error - try to remove item from empty array"); 
     exit(1); 
    } 
#endif 
    da_type ret_value = da->data[0]; 
    da->length -= 1; 
    da->data += 1; 
    ptrdiff_t offset = da->data - da->start; 
    if(offset > ALLOC_BLOCK_SIZE) 
    { 
     ptrdiff_t newsize = da->end - da->start - ALLOC_BLOCK_SIZE; 
     da_type *ptr = realloc(da->start, newsize * sizeof(da_type)); 
     if(!ptr) exit(1); 
     memmove(ptr + offset - ALLOC_BLOCK_SIZE, ptr + offset, da->length * sizeof(da_type)); 

     da->data = ptr + offset - ALLOC_BLOCK_SIZE; 
     da->start = ptr; 
     da->end = ptr + newsize; 
    } 
    return ret_value; 
} 

// Append array t to s 
void da_array_append(da *s, const da_type *t, size_t t_len) 
{ 
    if((s->length + t_len) > (s->end - s->start)) 
    { // Should reserve more space in the heap 
     ptrdiff_t offset = s->data - s->start; 
     ptrdiff_t newsize = s->length + t_len; 
     // Guarantees that new size is multiple of alloc block size 
     if(t_len % ALLOC_BLOCK_SIZE != 0) 
      newsize += ALLOC_BLOCK_SIZE - (t_len % ALLOC_BLOCK_SIZE); 

     da_type *ptr = malloc(newsize * sizeof(da_type)); 
     if(!ptr) exit(1); 

     memcpy(ptr, s->data, s->length * sizeof(da_type)); 
     memcpy(ptr + s->length, t, t_len * sizeof(da_type)); 
     free(s->start); 
     s->data = ptr; 
     s->start = ptr; 
     s->end = ptr + newsize; 
    } 
    else 
     // Enough space in heap buffer -> do it the simple way 
     memmove(s->data + s->length, t, t_len * sizeof(da_type)); 

    s->length += t_len; 
} 

// Append a da is a particular case of appending an array 
#define da_append(s, t) da_array_append(s, (t)->data, (t)->length) 

ответ

7

Что бы я хотел сделать, это вернуться к препроцессорному хакеру. Вы можете определенно получить что-то вроде шаблонов на C++, добавив информацию о типе, когда это необходимо.

struct da_impl { 
    size_t len; 
    size_t elem_size; 
    size_t allocsize; // or whatever 
}; 

void da_init_impl(struct da_impl *impl, size_t elem_size) 
{ 
    impl->len = 0; 
    impl->elem_size = elem_size; 
    impl->allocsize = 0; 
} 

#define DA_TEMPLATE(t) struct { da_impl impl; t *data; } 
#define da_init(a) da_init_impl(&a.impl, sizeof(*a.data)) 

// etc. 

Затем вы можете использовать его как это:

DA_TEMPLATE(int) intArray; 
da_init(intArray); 

da_append(intArray, 42); 

int foo = intArray.data[0]; 

Один недостаток заключается в том, что это создает анонимную структуру, вы не можете использовать за пределами своей сферы, но, может быть, вы можете жить с этим ...

+0

Разве ваш мозг сделан из кремния? ;-) –

+0

@FiddlingBits :) К сожалению, несмотря на то, что «другие» (khm ...) являются входящими ...:/ –

+1

Пока вы смеетесь по дороге в банк ... несколько точек репутации не будут дело. :-D –

2

Используйте объединение для общих данных ...

typedef union 
{ 
    int *pIntArr; 
    double *pDblArr; 
    struct *pStructArr; 
    ... // Etc. 
} genericData; 

... и использовать-структуру, чтобы держать это так, вы можете также включить какие данные вашего общего союз данных держат и его длину.

typedef struct 
{ 
    genericData data; 
    int dataType; // 0 == int, 1 == double, 2 == etc. 
    int dataLength; // Number of elements in array 
} genericDataType; 
+1

Что делать, если вы хотите сохранить действительно большую структуру ?, вы будете тратить память на хранение небольших объектов. Кроме того, вашему решению требуется коммутатор позже в коде. – this

+1

@self. Не уверен, что вы имеете в виду, тратя память. –

+0

Насколько велико объединение с указателем char и массивом символов размером 1000? – this

Смежные вопросы