6

私はこのメッセージを受け取りました:

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 ですか? 私は void ポインターを逆参照していますね。

みんなありがとう


2010 年 12 月 10 日 4:45PM EST 編集:
指摘されているようfree(NULL)に安全であり、C 標準でカバーされています。また、以下で説明するように、上記の私の例は合法的な C ではありません。caf の回答、Zack の回答、および私自身の回答を参照してください。

したがって、malloc されるポインターを NULL として初期化してから、コード内で free() と NULL を直接出力する方が簡単です。

free( pointer );
pointer = NULL;

私が行ったように myfree() で NULL をチェックしていた理由は、fclose() の経験によるものでした。fclose(NULL)プラットフォームによってはセグメンテーション違反が発生する可能性があるため (例: xpsp3 msvcrt.dll 7.0.2600.5512)、同じことが free() でも発生する可能性があると (誤って) 想定していました。関数でより適切に実装できるifステートメントでコードを乱雑にするのではなく、考えました。

皆様、良い議論をありがとうございました

4

4 に答える 4

4

いいえ、オブジェクトのアドレスを渡さない限り、これは正当な C ではありません(そのため、元の定義を保持することもできます)。void *myfree()

その理由は、あなたの例では、型char *のオブジェクト (のように宣言されたオブジェクトtest)が型の左辺値 (の左辺main()値) によって変更されるためです。C標準の§6.5は次のように述べています。void **vmyfree()

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 *、この制約は破られています。2 つのポインター型が互換性を持つための条件は、§6.7.5.1 で説明されています。

2 つのポインター型に互換性を持たせるには、両方が同じように修飾され、両方が互換性のある型へのポインターでなければなりません。

必要な効果を得るには、マクロを使用する必要があります。

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

NULL( は有効であるため、チェックする必要はありませんfree(NULL)。このマクロはp2 回評価されることに注意してください)。

于 2010-12-06T22:12:25.437 に答える
2

これは完全に合法ですが、あなたのコードを読んだ他の人を混乱させる可能性があります。

キャストを使用して警告を排除することもできます。

myfree((void **)&rest);

これはより読みやすく、理解しやすいものです。

于 2010-12-06T21:46:56.707 に答える
1

cafの答えは正しいです:いいえ、それは合法ではありません。そして、ザックがこのように法律を破ることが問題を引き起こす可能性が最も低いと指摘しているように。

comp.lang.c FAQリスト・質問4.9で、別の解決策と思われるものを見つけました。これは、中間のvoid値を使用する必要があることを示しています。

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


于 2010-12-10T21:36:14.987 に答える
1

C では、ここのどこかにキャストを導入するしかありません。マクロを使用して、呼び出しサイトでの処理が正しく行われたことを確認します。

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

[C の非無限マクロ再帰規則のおかげで、実際には関数とマクロの両方に「myfree」という名前を付けることができます! しかし、人間の読者にとっては混乱を招くでしょう。cafの回答の下の長い議論に従って、ここでのステ​​ートメントはエイリアス*ptr = 0を介して未知の型のオブジェクトを変更することも規定します。void**これは、プレーン C で利用できる最も悪いオプションです。引数を 2 回評価する caf のマクロは、(私にとって) 実際の問題を引き起こす可能性がはるかに高いようです。]

C++ では、テンプレート関数を使用できます。これは、3 つの点で優れています。呼び出しサイトで何かのアドレスを取得する必要がなくなり、型の正確性が失われず、コンパイル時エラーが発生します。へのポインタ以外を誤って渡した場合の実行時クラッシュmyfree

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

しかしもちろん、C++ では、スマート ポインターやコンテナー クラスなど、さらに優れたオプションを利用できます。

最後に、熟練した C プログラマーはこの種のラッパーを避けていることに注意してください。なぜなら、解放したばかりのメモリへのポインタの別のコピーがどこかにぶら下がっている場合には役に立たないからです。まさに助けが必要なときです。

于 2010-12-06T22:13:10.173 に答える