2010-09-18 4 views
3

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

В моей программе я создавал массив структур, перераспределяя пространство для массива каждый раз, когда я хотел добавить к нему структуру. Вот общая версия моей структуры и функции, которая бы добавить на структуру в массив:

typedef struct Example { 
    const char* name; 
    int (*func)(int, int); 
    int bool_switch; 
} 

int add_struct_to_array(Example **example_array, int *ex_array_size, int name, int (*func)(int, int), int bool_switch) 
{ 
    // first, make a new struct 
    Example *new_example = (Example *) calloc(1, sizeof(Example)); 
    if(new_example != NULL) { 
     new_example->name = name; 
     new_example->func = func; 
     new_example->bool_switch = bool_switch; 
     (*ex_array_size)++; 
    } else { 
     printf("Errror allocating %s\n", name); 
     exit(-1); 
    } 

    // now, realloc the array of structs and add the new member to it 
    Example **temp_example_array = (Example**)realloc(example_array, (*ex_array_size) * sizeof(Example*)); 
    if(temp_example_array != NULL) { 
     example_array = temp_example_array; 
     example_array[ (*ex_array_size) - 1 ] = new_example; 
    } else { 
     printf("Reallocation failed\n") 
     exit(-1); 
    } 
    return 0; 
} 

И вот здесь я бы назвал функции (обратите внимание, как я сначала выделить массив структур, Потому что вот где проблема была)

#include "example_struct.h" 

int main(int argc, char **argv) 
{ 
    int ex_array_size = 0; 
    Example **example_array = (Example**)calloc(0, sizeof(Example*)); 

    add_struct_to_array(example_array, &ex_array_size, "name", &function, 1); 
    ... 
    ... 
    add_struct_to_array(example_array, &ex_array_size, "other_name", &other_func, 0); 

    /* Do stuff here */ 

    example_array_free(example_array); 

    return 0; 
} 

в своем невежестве, я, видимо, считал, что выделение массива с размером 0 будет нормально, так как она изначально была пуста, и я мог бы добавить к нему структур после этого. Очевидно, что это не сработало, я получаю ошибки времени выполнения около error for object 0x100100080: pointer being reallocated was not allocated. example_array был по адресу 0x100100080, и первая структура, которую я выделил бы, была бы по адресу 0x100100090, а после нескольких перераспределений example_array закончится.

Итак, наконец, на мой вопрос. Я решил эту проблему, выделив больше места для моего example_array, чем мне нужно, но это кажется очень неэлегантным. Есть лучший способ сделать это?

** EDIT **

Итак, от взглядов большинства ответов, я не должен быть с помощью указателей на указатели. Итак, я пробую это несколько иначе, смешивая ответы pmg и crypto. Вот мой код прямо сейчас:

/* example_struct.h */ 
int add_struct_to_array(Example *example_array, int *ex_array_size, int name, int (*func)(int, int), int bool_switch) 
{ 
    Example temp_example_array = realloc(example_array, ((*ex_array_size) + 1) * sizeof(Example)); 

    if(temp_example_array != NULL) { 
     example_array = temp_example_array; 
     Example new_example; 
     new_example.name = name; 
     new_example.func = func; 
     new_example.bool_switch = bool_switch; 
     example_array[ (*ex_array_size) ] = new_example; 
     ++(*ex_array_size); 
    } else { 
     fprintf(stderr, "Error reallocating for %s", name); 
     exit(-1); 
    } 
    return 0; 
} 



/* main.c */ 
... 
... 
#include "example_struct.h" 
int main(int argc, char **argv) 
{ 
    int ex_array_size = 0; 
    Example *example_array = NULL; 

    add_struct_to_array(example_array, &ex_array_size, "name", &func, 1); 
    add_struct_to_array(...); 
    ... 
    add_struct_to_array(example_array, &ex_array_size, "other name", &other_func, 0); 

    example_free(example_array); 
} 

Все компилирует и realloc «s хорошо, но у меня есть проблемы с доступом к структурам в массиве.

/* main.c */ 
... 
... 
#include "example_struct.h" 
int main(int argc, char **argv) 
{ 
    int ex_array_size = 0; 
    Example *example_array = NULL; 

    add_struct_to_array(example_array, &ex_array_size, "name", &func, 1); 
    add_struct_to_array(...); 
    ... 
    add_struct_to_array(example_array, &ex_array_size, "other name", &other_func, 0); 


    printf("%s\n", example_array[0].name) /* Segfault */ 


    example_free(example_array); 
} 

Еще раз спасибо за помощь.

+0

Зачем нужен указатель на указатель на пример? Одного указателя достаточно, чтобы сделать как можно больше «массива». – pmg

+0

Не следует ли использовать строку 2 в функции add_struct_to_array() (пример *) как typecast, а не (пример *)? – 2010-09-18 15:42:10

+0

Совет. Вам не нужно явно указывать void *, возвращаемый calloc, malloc или realloc. Прочитайте это: http://stackoverflow.com/questions/1835193/is-it-a-better-practice-to-typecast-the-pointer-returned-by-malloc – 2010-09-18 15:53:11

ответ

0

Вот минимальная рабочая версия (с ключевыми словами на C++ для большого количества идентификаторов - я извиняюсь, но мне было интересно, когда я начал, и я не мог остановиться или вернуться на полпути), который также работает на ideone (http://ideone.com/iMByR)

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

struct protected { 
    int this; 
    int (*catch)(int, int); 
    int friend; 
}; 

int catch(int mutable, int virtual) { 
    return mutable + virtual; 
} 

struct protected *add_one(struct protected **private, 
          int *explicit, int using, 
          int (*catch)(int, int), int friend) { 
    struct protected *new; 

    new = realloc(*private, (*explicit + 1) * sizeof *new); 
    if (new) { 
    *private = new; 
    (*private)[*explicit].this = using; 
    (*private)[*explicit].catch = catch; 
    (*private)[*explicit].friend = friend; 
    (*explicit)++; 
    } 
    return new; 
} 

/* create an array of structs using dynamic memory */ 
/* keep adding elements to it, and growing it as needed */ 
int main(void) { 
    int using; 
    /* explicit contains the number of elements in the try array */ 
    int explicit = 0; 
    struct protected *try = NULL; 

    /* create and grow */ 
    for (using = 0; using < 7; using++) { 
    if (add_one(&try, &explicit, using + 1, catch, 0) == NULL) { 
     fprintf(stderr, "failure at loop %d\n", using); 
     exit(EXIT_FAILURE); 
    } 
    } 

    /* verify */ 
    for (using = 0; using < explicit; using++) { 
    printf("%d: %d\n", using, try[using].this); 
    } 

    free(try); 
    return 0; 
} 
+0

ha! У меня почти то же самое, за исключением строк - это функция add_one, в которой у вас есть '(* private) [* explicit] .this = ... и т. Д. У меня не было' * private' в parens и было получить segfault ... спасибо за помощь, и btw люблю ключевые слова C++ в качестве идентификаторов! –

+0

Хе-хе, не делайте этого часто жестко (ключевые слова C++ как идентификаторы C): вы создадите больше врагов, чем друзей: D – pmg

4

realloc принимает значение NULL в качестве значения указателя очень тонкой ... и делает malloc в этом случае

*p = NULL; 
new = realloc(p, 42); /* same as new = malloc(42); */ 
if (!new) { /* error */ } 
p = new; 

Итак, забудьте о calloc (вы перезаписать нули сразу после, во всяком случае), инициализировать указатели до NULL и realloc по желанию.

int main(void) { 
    Example *example_array = NULL; 
    add_struct_to_array(&example_array, &ex_array_size, "name", function, 1); 
    /* ... */ 
    free(example_array); 
} 
+0

Аккуратный трюк с использованием NULL. На странице man realloc: «Если значение ptr равно NULL, оно должно быть возвращено предыдущим вызовом malloc(), calloc() или realloc()». – 2010-09-18 15:55:28

+0

и до этого он (man 3 calloc) говорит: «Если ptr равно NULL, тогда вызов эквивалентен malloc (размер)» – pmg

+0

Я немного изменил свой код, чтобы посмотреть на мой отредактированный вопрос? –

0

Похоже, что std :: vector идеально подойдет вашим потребностям. Это так же быстро, как массив, и знает, как управлять памятью. Ниже приведен простой пример.

Вектор использует непрерывную память так же, как массив. Если вы знаете, сколько объектов Example необходимо, вы можете указать вектор, насколько он должен быть в конструкторе. Если вы не знаете, сколько будет вектор, он будет расти в соответствии с вашими потребностями. Если производительность важна, попробуйте выделить общее количество памяти в конструкторе, потому что когда вектор перераспределяет, он создаст новый кусок непрерывной памяти и затем скопирует каждый объект в новую область памяти (примеры конструктора и деструктора будут выполняться, когда это бывает).

Ознакомьтесь с различными конструкторами, емкостью() и резервом() вектора here.

#include <vector> 

int function_1(int a, int b) { 
    return 100; 
} 

int function_2(int a, int b) { 
    return 200; 
} 

typedef struct { 
    int name; 
    int (*func)(int, int); 
    int bool_switch; 
} example_t; 

typedef std::vector<example_t> example_container_t; 

int main() { 
    example_container_t container; 

    example_t example_1; 
    example_1.name = 1; 
    example_1.func = &function_1; 
    example_1.bool_switch = true; 
    container.push_back(example_1); 

    example_t example_2; 
    example_2.name = 1; 
    example_2.func = &function_1; 
    example_2.bool_switch = true; 
    container.push_back(example_2); 
    return 0; 
} 
+2

Что заставляет вас думать, что W_P хочет изменить свой код на 'C++'? :) – pmg

+0

Возможно, я ошибаюсь, но не #include C++? – 2010-09-18 16:00:24

+1

Источник очень похож на пример кода (т. Е. Класс примера, не имеет реальных функций, не компилируется и т. Д.). Я подумал, что, возможно, W_P был открыт для предположения, находящегося на таком раннем этапе. @W_P - извиняюсь, если это не так. – skimobear

1

Проследуйте следующие изменения. Вам не нужно выделять дополнительное пространство.

Edit: Добавление изменений предложенный PMG & Барт ван Ingen Schenau

int add_struct_to_array(Example ***example_array, int *ex_array_size, int name, int (*func)(int, int), int bool_switch) 
{ 
    Example **temp_example_array = realloc(*example_array,((*ex_array_size) + 1) * sizeof(Example *)); 
    Example *new_example = calloc(1, sizeof(Example)); 

    if(temp_example_array != NULL && new_example != NULL) { 
     *example_array = temp_example_array; 
     *example_array[ *ex_array_size ] = new_example; 
     new_example->name = name; 
     new_example->func = func; 
     new_example->bool_switch = bool_switch; 
     (*ex_array_size)++; 
    } else { 
     printf("Error allocating %s\n", name); 
     exit(-1); 
    } 
    return 0; 
} 


#include "example_struct.h" 

int main(int argc, char **argv) 
{ 
    int ex_array_size = 0; 
    Example **example_array = calloc(0, sizeof(Example*)); 

    add_struct_to_array(&example_array, &ex_array_size, "name", &function, 1); 
    ... 

    add_struct_to_array(&example_array, &ex_array_size, "other_name", &other_func, 0); 

    ... 

    example_array_free(example_array); 

    return 0; 
} 

Чтобы избежать нарушения любого из вашего другого кода, я использовал example_array указатель на указатель. Хотя лучшим решением было бы просто использовать указатель на struct, & сохранить realloc() пространство для него.

+2

В принципе НИКОГДА не назначайте результат realloc указателю, который вы перераспределяете. Это приводит к утечке памяти, когда 'realloc' терпит неудачу, потому что вы перезаписываете старое значение с помощью NULL – pmg

+0

, но там, где криптография присваивает результат realloc указателю, который он перераспределяет, защищен' if (temp_arry! = NULL) ' , поэтому, если 'realloc' терпит неудачу, он ничего не перезапишет ... –

+0

Мне нравится ваш ответ лучше всего, но я все еще получаю ошибки во время выполнения:/ –

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