2010-12-06 7 views
6

косвенного Я получил это сообщение:C вопрос: одиночные разыменовать на ничтожный ** двойного указателя

expected 'void **' but argument is of type 'char **' 

, когда я пытался собрать что-то похожее на это:

void myfree(void **v) 
{ 
    if(!v || !*v) 
     return; 

    free(*v); 
    *v = NULL; 

    return; 
} 



Я нашел что я думаю, это решение после прочтения этого вопроса о переполнении стека:
Avoid incompatible pointer warning when dealing with double-indirection - Stack Overflow

Так я приспособил к чему-то вроде этого:

#include <stdio.h> 
#include <stdlib.h> 

void myfree(void *x) 
{ 
    void **v = x; 

    if(!v || !*v) 
     return; 

    free(*v); 
    *v = NULL; 

    return; 
} 

int main(int argc, char *argv[]) 
{ 
    char *test; 

    if((test = malloc(1))) 
    { 
     printf("before: %p\n", test); 
     myfree(&test); 
     printf("after: %p\n", test); 
    } 

    return 0; 
} 

Законно ли это C? Я разыскиваю указатель на пустоту, не так ли?

Спасибо, ребята


EDIT 12/10/2010 4:45 вечера EST:
Как уже было отмечено, free(NULL) безопасен и покрыт стандартом C. Кроме того, как обсуждалось ниже, мой пример выше не является законным C. См. Ответ в кафе, ответ Зака ​​и мой собственный ответ.

Поэтому это будет легче для меня, чтобы инициализировать любые чтобы быть-malloc'd указатели как NULL, а затем в дальнейшем просто бесплатно() и NULL непосредственно в коде:

free(pointer); 
pointer = NULL; 

Причина, по которой я проверял NULL в myfree(), как и я, был из-за моего опыта работы с fclose(). fclose(NULL) может segfault в зависимости от платформы (например, xpsp3 msvcrt.dll 7.0.2600.5512), и поэтому я предположил (ошибочно) то же самое может случиться со свободным(). Я вычислил, а не загромождал свой код, если бы я мог лучше реализовать функцию.

Спасибо всем за все хорошее обсуждение

+3

Некоторые будут называть меня язычником, но я бы просто использовать `определить MyFree (х) делать {свободный (х); x = NULL} while (0) ` – 2010-12-06 22:06:13

+1

Вы не разыскиваете« указатель на void ». Вы разыскиваете «указатель на указатель на void», который является совершенно законным. – aschepler 2010-12-06 22:12:28

+1

На самом деле, я хочу исправить свой предыдущий макрос: `#define myfree (x) do {void ** tmp_ = & x; бесплатно (* tmp_); * tmp_ = NULL; } while (0) `(я думал, что не было возможности избежать двойной оценки` x`, но есть!) – 2010-12-06 22:19:18

ответ

4

Нет, это не правовой C, если не передать адрес void * объекта myfree() (так что вы можете также просто держать свое первоначальное определение).

Причина заключается в том, что в вашем примере, объект типа char * (объект объявлена ​​как test в main()) модифицируются через объект типа void * (в именующем *v в myfree()). § 6.5 стандартных состояний C:

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

— a type compatible with the effective type of the object, 
— a qualified version of a type compatible with the effective type of 
the object, 
— a type that is the signed or unsigned type corresponding to the effective 
type of the object, 
— a type that is the signed or unsigned type corresponding to a qualified 
version of the effective type of the object, 
— an aggregate or union type that includes one of the aforementioned 
types among its members (including, recursively, a member of a subaggregate 
or contained union), or 
— a character type. 

С void * и char * не являются совместимыми типами, это ограничение было нарушено. Условием для двух типов указателей, чтобы быть совместимым описан в §6.7.5.1:

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

Для достижения желаемого эффекта, необходимо использовать макрос:

#define MYFREE(p) (free(p), (p) = NULL) 

(Там нет необходимости проверять NULL, поскольку free(NULL) является легальным Обратите внимание, что этот макрос оценивает p дважды.).

2

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

Вы также можете использовать отливку для устранения предупреждения:

myfree((void **)&rest); 

Это более читаемым и понятным.

1

В C у вас нет другого выбора, кроме как представить что-то здесь. Я хотел бы использовать макрос, чтобы убедиться, что все было сделано правильно, на месте вызова:

void 
myfree_(void **ptr) 
{ 
    if (!ptr || !*ptr) return; 
    free(*ptr); 
    *ptr = 0; 
} 
#define myfree(ptr) myfree_((void **)&(ptr)) 

[Вы могли бы на самом деле назвать как функцию и макро «MyFree», благодаря C НИКАКОГО-бесконечномерным макро-рекуррентные правила ! Но это было бы странно для читателей. В течение долгого обсуждения ниже ответа кафе я также оговариваю, что в заявлении *ptr = 0 здесь изменен объект неизвестного типа через псевдоним void**, который является неопределенным поведением во время выполнения, однако мое информированное мнение таково, что это не вызовет проблем на практике , и это наименее плохая опция, доступная в простой C; что макрос, который оценивает его аргумент дважды, кажется гораздо более вероятным (для меня), чтобы вызвать реальные проблемы.]

В C++ вы можете использовать функцию шаблона, которая лучше по трем пунктам: она избегает необходимости принимать адрес чего-либо на сайте вызова он не нарушает правильность типа, и вы получите ошибку времени компиляции вместо сбоя во время выполнения, если вы случайно передаете не указатель на myfree.

template <typename T> 
void 
myfree(T*& ptr) 
{ 
    free((void *)ptr); 
    ptr = 0; 
} 

Но, конечно же, на C++ у вас есть еще лучшие варианты, такие как классы умных указателей и контейнеров.

Следует, наконец, упомянуть, что опытные программисты C избегают такого рода обертку, потому что это не поможет вам, когда есть еще одна копия указателя на память, которую вы только что освободили, где-то где-то висели - и это точно, когда вы нужна помощь.

1

ответ кафе правильный: Нет, это не является законным. И поскольку Зак указывает на нарушение закона таким образом, по-видимому, наименее вероятно, что вызовет проблемы.

Я нашел то, что кажется другим решением в comp.lang.c FAQ list · Question 4.9, в котором отмечается, что необходимо использовать промежуточное значение пустоты.

#include <stdio.h> 
#include <stdlib.h> 

void myfree(void **v) 
{ 
    if(!v) 
     return; 

    free(*v); 
    *v = NULL; 

    return; 
} 

int main(int argc, char *argv[]) 
{ 
    double *num; 

    if((num = malloc(sizeof(double)))) 
    { 
     printf("before: %p\n", num); 

     { 
      void *temp = num; 
      myfree(&temp); 
      num = temp; 
     } 

     printf("after: %p\n", num); 
    } 

    return 0; 
} 


 Смежные вопросы

  • Нет связанных вопросов^_^