2010-12-06 9 views
6
私はこのメッセージを受け取りました:

C質問:空白の単一参照解除**二重間接ポインタ

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




だから私は次のように調整した:

#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ポインタを逆参照していますか?

ありがとう



EDIT 12/10/2010 4:45 PM EST:
free(NULL)は安全でC規格の対象であることが指摘されています。また、以下に述べるように、上記の私の例は合法ではありません。カフェの答え、ザックの答え、自分の答えを見てください。

それゆえ、mallocされたポインターをNULLとして初期化し、後でfree()とNULLをコードに直接書き出すほうが簡単になります:

free(pointer); 
pointer = NULL; 

私が行ったようにmyfree()でNULLをチェックしていた理由は、fclose()での私の経験のためでした。 fclose(NULL)はプラットフォーム(例えばxpsp3 msvcrt.dll 7.0.2600.5512など)によってsegfaultすることができるので、私は(間違って)同じことがfree()で起こる可能性があると想定していました。私は関数内でよりよく実装できるif文を使って自分のコードを混乱させるよりもむしろわかっていました。

良いお話をいただき、ありがとうございます。

+3

(x)は、(x)の自由{行う一部は異教徒私を呼び出しますが、私はちょうどがmyfreeを定義し '使用します。 x = NULL} while(0) ' –

+1

あなたは 'voidへのポインタ'を逆参照していません。あなたは完全に合法である 'voidへのポインタへのポインタ'を逆参照しています。 – aschepler

+1

実際には、以前のマクロを修正したいと思います: '#define myfree(x)do {void ** tmp_ =&x;無料(* tmp_); * tmp_ = NULL; } while(0) '(私は' x'の二重評価を避ける方法はないと思ったが、そこにある!) –

答えて

4

ありませんが、これは、ない法的Cです。

理由はあなたの例では、タイプchar *のオブジェクトが(オブジェクトがmain()testとして宣言)タイプvoid *(左辺値myfree()*v)の左辺値によって変更されることです。 §6。void *char *ため

— 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. 

:オブジェクトが格納された値が1 、次のタイプを有する左辺値 発現によってのみアクセスしなければならない

7:C標準状態の5互換性のない型である場合、この制約は壊れています。適合するための2つのポインタ型の条件は、§6.7.5.1に記載されている:

2つのポインタ型の両方が同じ 修飾されなければならないとの両方が、互換性のある型へのポインタ なければならない、互換 することができます。 (free(NULL)が合法であることから、NULLをチェックする必要はありません。このマクロは二回p評価していることに注意してください。)

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

:あなたが望む効果を達成するために

は、あなたがマクロを使用する必要があります。

+0

オブジェクトへのポインターを* void *に変換し、情報を失うことなく戻す必要があります。したがって、 'void * 'エイリアスを介してオブジェクトへのポインタをクリアすることは、すべてのポインタが同じ表現をしていなくても圧倒的にDTRTになる可能性があります。唯一の場合は、* nullポインタの定数*がすべてのポインタからオブジェクトへの型に対して全ビットゼロではなく、AFAIKの誰も - 本当に誰も - それ以上のことはありません。私は、タイプベースのエイリアシングルールfehに起因する過度の最適化についても心配する必要があると思います。 – zwol

+0

標準を引用しますか?これは正しいとは言えません... –

+0

さらに、 'malloc'と' free'のセマンティクス自体が、ヌルポインタ定数がすべてのポインタからオブジェクトへの同じビットパターンであることを必要とすることがあります。 – zwol

2

これは完全に合法ですが、あなたのコードを読んで、他の人のために混乱得ることができます。

また、警告を排除するために、鋳造使用することができます

myfree((void **)&rest); 

これは、より読みやすく、理解しやすいです。あなたは(あなたが同様にちょうどあなたの元の定義を維持する可能性がある)myfree()void *オブジェクトのアドレスを渡さない限り

+0

void **はポインタへの汎用ポインタではありません。 – liuyang1

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言語で利用可能な最も悪いオプションです。その引数を2度評価するカフェのマクロは、本当の問題を引き起こす可能性が非常に高いようです。]

C++では、3つのカウントでより良いテンプレート関数を使うことができます。コールサイトでは型の正確さが損なわれず、誤って非ポインタをmyfreeに渡すと、実行時クラッシュではなくコンパイル時エラーが発生します。

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

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

最終的には、熟練したCプログラマが、この種のラッパーを避けるべきだと言わなければなりません。解放されたばかりのメモリへのポインタの別のコピーがあるときは役に立ちません。助けが必要。

+0

私は関数とマクロを同じ名前で書くのが普通ですが、関数を呼び出すとき(マクロや他の場所で)、 '(myfree)(x);'と書くと、a)プリプロセッサマクロの展開を無効にします.b )は私がマクロを呼んでいないことを明確にし、c)関数ポインタとして 'myfree'の代わりに' myfree'を渡せるようにします。 –

+0

それは良いトリックだよね。 – zwol

+0

Oooh、私はマクロの中にキャストを隠す考えが好きです。マクロ内でNULLに設定を隠すよりも、ポインタが変更される重要な視覚的な手掛かりであるアドレス(&)演算子を保持するため、ここではあなたのやり方がはるかに良いです。 –

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


関連する問題