C Вопрос: Одиночный разрез на пустоте ** Указатель двойного подражания
-
09-10-2019 - |
Вопрос
Я получил это сообщение:
expected 'void **' but argument is of type 'char **'
Когда я пытался скомпилировать что-то подобное на это:
void myfree( void **v )
{
if( !v || !*v )
return;
free( *v );
*v = NULL;
return;
}
Я нашел то, что я думаю, это решение после прочтения этого вопроса о переполнении стека:
Избегайте несовместимого указателя, предупреждающего при работе с двойным нарушением - переполнение стека
Поэтому я приспособился к чему-то вроде этого:
#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? Я разыгрываю пустотный указатель не так ли?
Спасибо ребята
Редактировать 12/10/2010 16:45 PM EST:
Как это было указано free(NULL)
безопасен и охватывается стандартом C. Также, как обсуждалось ниже моего примера выше, не законно C. См. Ответ CAF, ответ Зак, и мой собственный ответ.
Поэтому для меня будет легче инициализировать какие-либо указатели на Malloc'd как NULL, а затем только бесплатно () и NULL прямо в коде:
free( pointer );
pointer = NULL;
Причина, по которой я проверял NULL в MyFreee (), как я, был из-за моего опыта с FCLOSE (). fclose(NULL)
Может SegFault в зависимости от платформы (например, xpsp3 msvcrt.dll 7.0.2600.5512) И поэтому я предполагал (по ошибке) то же самое может произойти с бесплатным (). Я подумал, а не загромождаю мой код с помощью утверждений, которые я могу лучше реализовать в функции.
Спасибо всем за все хорошее обсуждение
Решение
Нет, это нет юридический C, если вы не передадите адрес void *
Возражать myfree()
(Так что вы также можете просто сохранить ваше первоначальное определение).
Причина в том, что в вашем примере объект типа char *
(Объект объявлен как test
в main()
) изменяется через Lvalue типа void *
(Lvalue. *v
в myfree()
). §6.5 стандартных состояний C:
7 Объект должен иметь его сохраненное значение, доступное только выражением Lvalue, которое имеет один из следующих типов:
— 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
дважды).
Другие советы
Это совершенно законно, но может запутаться для других людей, которые читают ваш код.
Вы также можете использовать отливку для устранения предупреждения:
myfree((void **)&rest);
Это более читаемое и понятно.
В C у вас нет выбора, кроме как представить ряд где-то здесь. Я бы использовал макрос, чтобы убедиться, что все было сделано правильно на участке вызова:
void
myfree_(void **ptr)
{
if (!ptr || !*ptr) return;
free(*ptr);
*ptr = 0;
}
#define myfree(ptr) myfree_((void **)&(ptr))
Вы могли бы на самом деле назвать как функцию, так и в макросе «MyFree», благодаря правилам не бесконечно-макро-рекурсии C нет бесконечно-макроса! Но это будет запутано для человеческих читателей. На длительное обсуждение ниже ответа CAF, я также предусматриваю, что заявление *ptr = 0
здесь модифицирует объект неизвестного типа через void**
Псевдоним, которое представляет собой непреодолищенное поведение - однако, мое обоснованное мнение, оно не вызывает проблем на практике, и это наименее плохое вариант, доступное на простых C; Макрос CAF, который оценивает свой аргумент дважды, кажется гораздо более вероятным (для меня), чтобы вызвать реальные проблемы.
В C ++ вы можете использовать функцию шаблона, которая лучше на трех счетчиках: он позволяет избежать необходимости принимать адрес чего-либо на участке вызовов, он не нарушает тип правильности, и вы получите ошибку с компиляцией вместо сбой времени работы, если вы случайно пропустите не указатель на myfree
.
template <typename T>
void
myfree(T*& ptr)
{
free((void *)ptr);
ptr = 0;
}
Но, конечно, в C ++ у вас есть еще лучшие варианты, такие как умные указатели и классы контейнера.
Наконец, следует упомянуть, что опытные программисты C ischew такого рода обертки, потому что он не поможет вам, когда есть еще одна копия указателя на память, которую вы только что освободились, и именно где-то, когда вам нужна помощь.
Ответ CAF правильный: нет, это не законно. Отказ И как Zack указывает на нарушение закона таким образом, по-видимому, наименее вероятно, чтобы вызвать проблемы.
Я нашел то, что кажется другим решением в COMP.LANG.C FAQ Список · Вопрос 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;
}