2014-01-06 3 views
3

Так обучений указателей в C, и я подумал, что в качестве упражнения я мог бы сделать некоторый общий массив и я получил это работает при использовании аннулируется ** как это:Динамический массив с пустым указателем

struct array{ 
    void **data; 
    size_t size, capacity; 
}; 

вставляя элементы, как это :

void array_append(array *a, void *element){ 
    if(a->size == a->capacity){ 
     a->capacity += ARRAY_GROW_CONSTANT; 
     a->data = realloc(a->data, sizeof(void*)*a->capacity); 
    } 
    a->data[a->size++] = element; 
} 

Но это не очень хорошо. Массив хранит указатели на элементы, поэтому, когда объем элемента заканчивается, он становится недействительным, а также делает содержимое массива разбросанным по всей памяти. Это я думаю, что может быть решено путем выделения самих элементов, так вместо

a->data[a->size++] = element; 

я бы сделать что-то вроде

a->data[a->size] = malloc(inserted_element_size); 
memcpy(a->data[a->size], &element, inserted_element_size); 
size++; 

, но я думал, что я мог бы получить то же funcionality при использовании обычного недействительных *, вместо того, чтобы недействительными **

struct array{ 
    void *start; 
    size_t element_size, size; 
}; 

и вставляя элементы, такие как

void array_append(array *a, void *element){ 
    a->size += 1; 
    a->data = realloc(a->data, (a->size*a->element_size)); 
    memcpy(a->data + (a->size - 1)*a->element_size, &element, a->element_size); 
} 

но это приводит к segfault, и я не знаю почему. Поскольку я это понимаю (очевидно, я этого не делаю), указатели являются адресами в памяти, поэтому, если у меня есть непрерывный блок памяти, я могу хранить переменную любого типа в ней со смещением.

Редактировать: Спасибо за объяснение, это действительно помогло.

Что такое a-> data initialized to?

Я использовал функцию для инициализации массива, а a->data был инициализирован элементом element_size.

абонент должен будет бросить resutl в элемент *

Я думал, что я мог бы использовать макрос, чтобы сделать печатное короче (я думаю, что это плохо?), Но я не знать о производительности typecasting от void* до struct*.

Создание динамического массива элементов непосредственно представляется мне более практичным.

Но это не позволило бы мне использовать массив как общий? Что я хотел, чтобы определить общий массив, который я мог бы использовать для хранения любого типа, как

array *a1 = create_array(sizeof(int)); // array of int 
array *a2 = create_array(sizeof(double)); // array of double 
etc... 

почему вы хотите, чтобы ваши данные хранятся в непрерывном блоке?

Потому что я думал, что вам нужен непрерывный блок памяти для использования memcpy со смещением.

ответ

0

Что такое a->data Инициализировано? Чтобы это сработало, оно должно быть установлено в NULL, когда создается (пустой) массив.

Кроме того, вы обращаетесь к вычислениям, не учитывающим арифметику указателей. a->data - это указатель (для void *), поэтому смещение (a->size - 1)*a->element_size будет умножаться на размер указателя (до void *). Настройка a->data на void * должна приводить к ошибке компилятора с void не имеет размера.

Если вы действительно хотите сделать это, лучше объявить a->data как char *, который гарантированно имеет размер 1.

Осторожно: доступ к вашему массив потребует приведения к (element*). Это предотвратит использование квадратных скобок.
Вы должны будете предоставить функцию доступа, как void * array_at(size_t index) { return &a->data[index*a->element_size]; }
, а затем абонент должен будет бросить resutl в element *.

Создание динамического массива element s непосредственно кажется мне более практичным.
Вы все равно можете позвонить по телефону realloc, если хотите.

Но первый вопрос, который приходит мне на ум: почему вы хотите, чтобы ваши данные хранились в непрерывном блоке?

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

0

Комментирует последнюю часть кода, используя void * в качестве массива данных. Код должен работать, но он имеет проблемы:

Вы передаете адрес указателя элемента, а не только указатель, который уже указатель на исправление (надеюсь) данных.

Также вы не можете выполнять арифметику указателей на void, но некоторые компиляторы позволяют это.

Правильная версия тетсра будет

memcpy ((unsigned char*)a->data + (a->size - 1)*a->element_size, element, a->element_size); 
Смежные вопросы