Почему realloc() терпит неудачу там, где malloc() преуспевает в C?

На RHEL6 я столкнулся со странной проблемой с realloc(). В какой-то момент программы realloc() возвращает NULL (у старого указателя есть адрес и много доступной памяти). Выделяется 200 элементов структуры (структура ниже). По какой-то причине, когда я вместо этого делаю realloc(), это работает, но тогда мне приходится присваивать старый указатель новому. Ниже приведена упрощенная версия моего кода.

Возможно, это проблема настройки сервера, а не программирования. Каково ваше мнение?
Спасибо.

//hearder file
typedef struct  {        /* Variable Node Detail Record */
   long     next;
   long     mask;
   char     *value;   
   // more stuff...
} NODETEST;

extern NODETEST *oldNodes;
extern NODETEST *newNodes;

//program
#define MAXSIZE 200

// do some stuff with oldNodes....

int alloc_nodes (void)
{
    // Allocate or grow the table
    oldNodes = (NODETEST *) malloc(MAXSIZE * sizeof(NODETEST));
    if( oldNodes == NULL ) {
            //handle exception...
            exit(1);
      }

    //oldNodes = (NODETEST *) realloc(oldNodes,MAXSIZE * sizeof(NODETEST)); // *** FAILS

    newNodes = (NODETEST *) realloc(oldNodes,MAXSIZE * sizeof(NODETEST));   // *** WORKS

    if( newNodes == NULL ){
        printf("errno=%d\n", errno );
    }else{
        oldNodes = newNodes;            }
}

person user3772839    schedule 06.10.2014    source источник
comment
Используйте perror при сбое realloc или malloc (или calloc).   -  person Basile Starynkevitch    schedule 06.10.2014
comment
используйте perror и покажите нам, что он возвращает   -  person Farouq Jouti    schedule 06.10.2014
comment
Работает ли free на старом указателе?   -  person Marian    schedule 06.10.2014
comment
Строка кода, которую вы говорите, *** WORKS использует realloc(), а не malloc(). Я так понимаю это ошибка? Было бы неплохо скопировать/вставить код, с которым вы действительно видите проблему.   -  person Michael Burr    schedule 06.10.2014
comment
Я бы предложил использовать в коде valgrind; может быть больше, чем вы думаете.   -  person Jonathan Leffler    schedule 06.10.2014
comment
но затем я должен назначить старый указатель новому. - Вы должны сделать это и с realloc(), а не только с malloc().   -  person nos    schedule 06.10.2014
comment
Возможно, вы захотите пропустить свой реальный код. Вам не хватает точки с запятой в первом malloc. Скорее всего, ошибка в отсутствующем коде.   -  person Mark Lakata    schedule 06.10.2014
comment
Если единственная разница между работой и ошибкой заключается в том, что строка realloc закомментирована или нет, то ваша последняя строка if будет читать неинициализированный newNodes.   -  person Mark Lakata    schedule 06.10.2014
comment
Майкл, да, я имел в виду realloc.   -  person user3772839    schedule 06.10.2014
comment
Уточнение: код сначала вызывает malloc(), а затем изменяет размер с помощью realloc(). @nos: хорошая мысль.   -  person user3772839    schedule 06.10.2014
comment
@ user3772839: Комментарии в вашем коде предполагают, что realloc либо РАБОТАЕТ, либо НЕ РАБОТАЕТ, в зависимости от того, в какую переменную вы сохраняете результат? Это очень нереально. Я не верю, что код, который вы разместили выше, точно описывает ситуацию, с которой вы имеете дело.   -  person AnT    schedule 06.10.2014


Ответы (1)


Ваш первый вызов malloc с размером S, а затем realloc с тем же размером S. Это неправильно: вы должны передать realloc новый желаемый размер (независимо от текущего размера - это не приращение). Здесь есть большой шанс, что realloc вернет точно такой же указатель, который он получил. Кстати, неясно, почему вы хотите сделать с malloc, за которым сразу следует realloc. Дает нам больше деталей.

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

NODETEST *newNodes = NULL;
int allocated_elem = 0;
int n_elem = 0;

#define ALLOC_INCR 200

затем при каждом добавлении:

if (n_elem >= alloc_size) { // the first time realloc is as malloc since nodes == NULL
    alloc_size += ALLOC_INCR;
    nodes = (NODETEST *) realloc(nodes, alloc_size * sizeof(NODETEST));
    if (nodes == NULL) {
        //handle exception...
        exit(1);
    }
}
// add your element at nodes[n_elem]
n_elem++;

Напомним, что realloc действует как malloc, когда полученный указатель равен NULL (случай первого вызова). Таким образом он размещает исходную таблицу. Последующие вызовы перераспределяют его, регулируя размер с постоянным приращением (здесь 200). Возможны и другие схемы увеличения таблицы, например, вы можете умножить размер на коэффициент (например, 2), начиная с 32:

if (n_elem >= alloc_size) { // the first time realloc is as malloc since nodes == NULL
    alloc_size = (alloc_size == 0) ? 32 : alloc_size * 2;

Пересмотрите комментарии FAIL и WORKS: ясно, что если вы присваиваете oldNodes (в коде FAIL), то newNodes не присваивается и сохраняет свое начальное значение, равное нулю (NULL), поскольку оно объявлено как глобальная переменная и не инициализировано (ну Полагаю, здесь extern). Таким образом, тест if (newNodes == NULL), вероятно, провалится.

person didou    schedule 07.10.2014
comment
ваш код так близок к исходному, который у меня есть. На самом деле в моем коде realloc() использует размер, отличный от того, на который указывает существующий указатель. Я должен был улучшить свой пример, извините, я просто не могу опубликовать исходный код. В будущем я буду лучше работать со своими примерами, на этой доске так много отличных советов от таких людей, как вы. Спасибо еще раз. - person user3772839; 07.10.2014