Распределение памяти / вопрос перераспределения - PullRequest
3 голосов
/ 18 сентября 2010

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

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

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 в порядке, но у меня проблемы с доступом к структурам в массиве.

/* 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 );
}

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

Ответы [ 4 ]

4 голосов
/ 18 сентября 2010

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);
}
1 голос
/ 18 сентября 2010

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

Редактировать: Добавление изменений, предложенных pmg & Барт ван Инген Шенау

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. Хотя лучшим решением было бы просто использовать указатель на структуру и оставить для нее realloc () место.

0 голосов
/ 19 сентября 2010

Вот минимальная рабочая версия (с ключевыми словами 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 голосов
/ 18 сентября 2010

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

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

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

#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;
}
...