Использование malloc () для уникальных указателей - PullRequest
1 голос
/ 07 августа 2010

Итак, у меня есть функция, возвращающая указатель, или NULL при ошибке. Теперь я хотел бы добавить другое возможное значение для другой ошибки / условия. Я слышал, что это упоминалось в каком-то другом ответе ранее - используя malloc() для создания уникального указателя, который будет служить возможным значением для возврата таких функций (так что теперь можно вернуть правильный указатель NULL или 0xWhatever может быть уверен, что 0xWhatever не будет использоваться ни для чего другого). Поэтому, естественно, malloc(1), вероятно, безопасная ставка, но мне было интересно, безопасен ли malloc(0) и для этого. Будет ли адрес malloc(0) использоваться для чего-то еще? Может кто-нибудь уточнить, как эта техника должна работать в целом, и, возможно, как она называется?

Ответы [ 6 ]

5 голосов
/ 07 августа 2010

Из стандарта C99:

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

Вам лучше использовать malloc(1).Это гарантированно вернет ненулевое значение, если память доступна.

3 голосов
/ 07 августа 2010

Использование malloc для этой цели - очень плохая практика. Просто сделайте:

static const char myerror;
return (void *)&myerror;

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

Редактировать: Первоначально я пропустил &. Исправлено.

2 голосов
/ 07 августа 2010

Несколько касательно вопроса, но наличие «указателя условия ошибки» (отличного от NULL) на самом деле не является идиоматическим C. Если вы хотите написать функцию, которая выделяет память для указателя и может возвращать несколько различных состояний ошибки,есть еще два обычных способа сделать это:

  1. Установите errno для описания вашей конкретной ошибки и верните NULL для всех ошибок.
  2. Возьмите указатель науказатель в качестве аргумента и возврат целочисленного кода ошибки

Например

sometype* my_function()
{
  sometype* p; 

  /* Do something */

  if (error_1_occurred) {
     errno = EAGAIN; /* Or whatever is appropriate */
     return NULL;
  } else if (error_2_occurred) {
     errno = EINVAL; /* Or whatever is appropriate */
     return NULL;
  } 

  p = malloc(/* whatever */);

  /* Do stuff */

  return p;
}

или

int my_function(sometype** pp) {

  if (!pp) return -99;

  /* Do something */

  if (error_1_occurred) {
     return -1;
  } else if (error_2_occurred) {
     return -2;
  } 

  *pp = malloc(/* whatever */);

  /* Do stuff */

  return 0;
}
2 голосов
/ 07 августа 2010

Насколько я понимаю, в общем, malloc(0) будет "выделять" память, которая должна быть освобождена позже.Это даст вам место в памяти, в которое вы не должны писать.

Вам не обязательно использовать malloc() для получения уникального адреса, например, оно может быть получено из статического местоположения.Тогда вы можете использовать этот адрес в качестве вашего другого условия.Также не учитывается распределение.Я не помню термин для этого использования, что-то о прокси.

#define ERRORPTR ((void *)&_unusedvar)
static char _unusedvar;

if ( /* failed */ )
    return ERRORPTR;
1 голос
/ 07 августа 2010
Указатель

A NULL - это не что иное, как указатель на ячейку памяти с адресом 0, поскольку память обычно разбивается на части по 4 Кб, вы можете использовать числа от 1 до 4095, чтобы возвращать другие виды ошибок в вашей функции, потому что кусок памяти (0 - 4096) не будет выгружен для вашего приложения (если вы явно не отобразите его, используя mmap).

Так что было бы безопасно сделать что-то вроде:

if(someError)

  return NULL;

else if(someOtherError)

  return (void *)1;

else
  /* Do something else */
1 голос
/ 07 августа 2010

До, и я полагаю, включая C89, значение было не определено. Стандарту просто нечего было сказать о ненулевом (или, что еще хуже, отрицательном) размере. Я вполне уверен, что теперь это Реализация Определена. Используйте ненулевой размер для переносимости.

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

Другой традиционный подход (используемый в самых ранних версиях signal(), IIRC) заключался в приведении небольших целых чисел, отличных от 0, к типу указателя. На самом деле это относительно непереносимо, поскольку единственное целое число, которое переносимо и безопасно приводит к указателю, равно 0, что равно NULL.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...