Должен ли я осуществлять проверку realloc, если новый размер блока меньше исходного? - PullRequest
7 голосов
/ 30 марта 2010

Может ли в этом случае произойти сбой realloc?

int *a = NULL;

a = calloc(100, sizeof(*a));
printf("1.ptr: %d\n", a);

a = realloc(a, 50 * sizeof(*a));
printf("2.ptr: %d\n", a);

if(a == NULL){
    printf("Is it possible?\n");
}

return (0);

}

Вывод в моем случае:

1.ptr: 4072560
2.ptr: 4072560

Так что 'a' указывает на то же самоеадрес.Так что мне следует применять проверку realloc?

Позднее редактирование :

  • Использование компилятора MinGW под Windows XP.
  • Схоже ли поведение с gccв Linux?

Позднее редактирование 2: Можно ли проверить этот способ?

int *a = NULL, *b = NULL;

a = calloc(100, sizeof(*a));
b = realloc(a, 50 * sizeof(*a));

if(b == NULL){
    return a;
}
a = b;
return a;

Ответы [ 6 ]

7 голосов
/ 30 марта 2010

Да, вы всегда должны принудительно проверять realloc или любое другое распределение памяти в этом отношении.

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

Вполне вероятно, что это когда-нибудь потерпит неудачу? Наверное, нет, я был бы изумлен, если бы вы могли найти случай, который это делает. Однако это не значит, что не будет. Заключение realloc в функцию, которая автоматически выполняет проверку для каждой операции, достаточно просто, поэтому нет причин не делать этого.

void* xrealloc(void* ptr, size_t size) {
  ptr = realloc(ptr, size);
  if ( !ptr ) {
    exit(EXIT_FAILURE);
  }
  return ptr;
}
5 голосов
/ 30 марта 2010

Было бы удивительно, если бы realloc потерпел неудачу при передаче размера, меньшего, чем первоначальное распределение, но ничто в стандарте C (7.20.3.4) не гарантирует, что оно всегда будет успешным:

Функция realloc освобождает старый объект, на который указывают ptr и возвращает указатель на новый объект, который имеет размер, заданный size. содержимое нового объекта должно быть такой же, как у старого объекта до освобождения, до меньший из новых и старых размеров. любой байтов в новом объекте за пределами Размер старого объекта есть неопределенные значения.

Если ptr является нулевым указателем, то realloc функция ведет себя как malloc функция для указанного размера. В противном случае, если ptr не совпадает с указатель, ранее возвращенный calloc, malloc или realloc функция, или если пространство было освобождено по телефону free или realloc функция, поведение не определено. Если память для нового объекта не может быть выделенный, старый объект не освобожден и его значение без изменений.

Returns

Функция realloc возвращает указатель к новому объекту (который может иметь то же значение, что и указатель на старый объект), или нулевой указатель, если новый объект не может быть выделен.

Очень простая соответствующая реализация realloc будет такой:

void *realloc(void *ptr, size_t size)
{
    void *new_ptr= malloc(size);
    if (new_ptr && ptr)
    {
        size_t original_size= _get_malloc_original_size(ptr);
        memcpy(new_ptr, ptr, min(original_size, size));
        free(ptr);
    }

    return new_ptr;
}

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

Также было бы очень просто оптимизировать возврат того же указателя, если размер исходного выделения больше или равен запрошенному размеру. Но ничто в Стандарте C не требует этого.

2 голосов
/ 30 марта 2010

Хорошей практикой является проверка возвращаемого значения realloc в любом случае (в спецификации не сказано, что вы безопаснее, если вы уменьшаете блок памяти, чем если вы его расширяете). Но вы должны быть осторожны, чтобы НЕ потерять начальный указатель (что вы делаете, в вашем случае), так как тогда вы совершенно не сможете его освободить.

1 голос
/ 30 марта 2010

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

1 голос
/ 30 марта 2010

Стандарт C99 §7.20.3.4 (realloc) гласит:

Функция realloc освобождает старый объект, на который указывает ptr, и возвращает указатель на новый объект, размер которого указан по размеру. Содержание нового объект должен быть таким же, как и у старого объекта до освобождения, до новые и старые размеры. Любые байты в новом объекте, превышающие размер старого объекта, имеют неопределенные значения.

Если ptr является нулевым указателем, функция realloc ведет себя как функция malloc для указанный размер. В противном случае, если ptr не совпадает с указателем, ранее возвращенным функции calloc, malloc или realloc, или если пространство было освобождено вызовом для функции free или realloc поведение не определено. Если память для нового объект не может быть размещен, старый объект не освобожден и его значение не изменилось.

Возвращает

Функция realloc возвращает указатель на новый объект (который может иметь тот же значение как указатель на старый объект), или нулевой указатель, если новый объект не может быть выделено. * * 1 010

Обратите внимание, что старый объект освобожден; новый объект может оказаться в том же месте, что и старый. И могут быть проблемы. Это довольно маловероятно, но гораздо проще следовать правилу «всегда», чем иметь странные исключения.

Обычный контраргумент: «если это не может не получиться, значит, у меня есть путь ошибки, который я не могу проверить». До определенного момента, это правда. Тем не менее, может случиться так, что произошла некоторая утечка памяти, так что распределение не может быть успешным - потому что управляющая информация была повреждена. Скорее всего, вы просто получите дамп ядра, но, возможно, код достаточно надежен, чтобы этого избежать. (Я предполагаю, что жестко закодированные 100 и 50 предназначены для постановки вопроса; реальный код не будет перераспределять, когда знает, сколько ему действительно нужно.)

Там, где два вызова функции realloc () смежны, как здесь, очень мало места для того, чтобы что-то пошло не так. Однако в реальном рабочем коде между этими двумя операциями должно быть несколько операций, и этот код может вызвать сбой второго метода realloc ().

Относительно вашего «Правка 2» ...

Код может быть лучше написан как:

if (b != NULL)
    a = b;
return a;

Но основная концепция в порядке. Обратите внимание, что в стандарте прямо сказано, что исходное распределение безопасно, если новое не может быть создано.

0 голосов
/ 13 сентября 2014

realloc() может вернуть NULL достаточно легко при уменьшении размера.

void *ptr = malloc(10);
ptr = realloc(ptr, 0);
if (ptr == NULL) {
  puts("Failure because return value is NULL? - not really");
}

realloc(any_pointer, 0) может вернуть NULL или, может быть, какой-нибудь указатель not-NULL, это определяется реализацией.

Поэтому сбой realloc()/malloc() должен быть не простым тестом if (ptr == NULL), а

void *ptr = malloc(newsize); // or realloc(..., newsize)
if (ptr == NULL && newsize > 0) {
  exit(0); // Handle OOM;
}

Из-за этой неоднозначности, если код захочет создать оболочку realloc(), порекомендуйте что-то вроде:

void *xrealloc(void *ptr, size_t newsize, bool *falure) {
  *failure = 0;
  if (newsize > 0) {
    void *tmp = realloc(ptr, newsize);
    if (tmp == NULL) {
      *failure = 1;
      return ptr;  // old value
    }
    return tmp;  // new value
  } 
  free(ptr);
  return NULL; // new value
  }

Получение NULL на realloc() с уменьшенным размером, следовательно, на самом деле не сбой , и поэтому этот ответ применяется только косвенно, но вопрос OP был "... принудительно проверять realloc, если новый блок размер меньше исходного? и затем использовал менее надежную if (ptr == NULL) парадигму.

...