Reallo c возвращает NULL, но не устанавливает errno. Как правильно проверить эту ошибку / нечетное поведение? - PullRequest
0 голосов
/ 21 января 2020

Рассмотрим этот код:

main() {
   float *ptr = NULL;
   while(true) {
      ptr = (float *)realloc(ptr, 0*sizeof(float));
      fprintf(stdout,"Errno: %d, Ptr value: %d\n",errno, (int)ptr);
   }
}

Странно то, что errno никогда не устанавливается (по крайней мере, для меня), но вызов альтернативно возвращает NULL и значение указателя. Я думаю, что 0 выделений может вернуть ошибку, но не достаточно серьезную, чтобы установить errno. Или код с reallo c проблематичен c. Я не уверен.

Мне было бы все равно, но это вызывает у меня (0 байт) утечку памяти.

Вопрос 'Reallo c Failure' не совсем так же, как это в значительной степени предполагает, что NULL-возврат из reallo c () является ошибкой. Это не тот случай в этой ситуации. В основном это касается различного поведения reallo c (), когда ему передается нулевой размер.

Ответы [ 3 ]

3 голосов
/ 22 января 2020

Глядя на:

https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#__libc_realloc

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

Если, однако, входной указатель имеет значение NULL, то reallo c просто действует как mallo c и возвращает то, что производит mallo c (0). (В этом случае он не проверяет, равен ли размер 0 или нет.)

Итак, errno отсутствует, поскольку ошибки нет. Но reallo c (), возвращающее NULL, не обязательно является ошибкой, что является нюансом, который я не распознал.

Итак, в этом примере первый вызов (размер 0, NULL ptr) возвращает выделенное указатель на область данных нулевого размера. При втором вызове (размер 0, не NULL ptr) процедура освобождает указатель и возвращает NULL. И тогда цикл повторяется.

1 голос
/ 22 января 2020

realloc возвращает NULL, но не устанавливает errno. Как правильно проверить наличие этой ошибки / нечетного поведения?

Для realloc(), errno не указано для установки по стандарту C. Любая настройка errno является поведением, определяемым реализацией.

C действительно указывает:

Если размер запрошенного пространства равен нулю, поведение определяется реализацией: либо возвращается нулевой указатель, указывающий на ошибку, либо поведение такое, как если бы размер был некоторым ненулевым значением, за исключением того, что возвращенный указатель не должен использоваться для доступа к объекту. C17 / 18 § 7.22.3 1

Если размер равен нулю и память для нового объекта не выделена, определяется реализацией, был ли освобожден старый объект. C17 / 18 § 7.22.3.5 3

Попробуйте избежать 3 поведения, определяемого реализацией баллов. Используйте вспомогательную функцию и вызовите free(), когда новый размер равен 0.

// Return error status
bool realloc_float(float **ptr, size_t new_size) {
  // Size zero or too big ....
  if (new_size == 0 || new_size > SIZE_MAX/sizeof(float)) {
    free(*ptr);
    *ptr = NULL;
    return new_size > 0;  // fail on large new_size
  }
  float *newp = realloc(*ptr, sizeof(float) * new_size);
  if (newp == NULL) {
    free(*ptr);
    *ptr = NULL;
    return true; // failure
  }
  *ptr = newp;
  return false;
}

Использование

int main() {
   float *ptr = NULL;
   for (int i = 0; i < 10; i++) {
      size_t sz = rand()%4; 
      bool err = realloc_float(&ptr, sz);
      printf("Error: %d, Ptr value: %p, size %zu\n", err, (void*)ptr, sz);
   }
   free(ptr); 
}
0 голосов
/ 21 января 2020

Просто используйте промежуточную переменную. Например,

int main( void ) {
   float *ptr = NULL;
   while(true) {
      float *tmp = realloc(ptr, 0*sizeof(float));

      if ( !tmp ) 
      {
          // report an error
          break;
      }

      ptr = tmp;
      //...
   }
}

Что касается запроса памяти нулевого размера, то в соответствии со стандартом C (7.22.3 Функции управления памятью)

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

...