C Вопрос: Одиночный разрез на пустоте ** Указатель двойного подражания

StackOverflow https://stackoverflow.com/questions/4371232

Вопрос

Я получил это сообщение:

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;
}


Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top