web-dev-qa-db-ja.com

Cでは、ポインタを解放する前にキャストする人がいるのはなぜですか?

私は古いコードベースに取り組んでおり、free()のほとんどすべての呼び出しは引数にキャストを使用しています。例えば、

free((float *)velocity);
free((float *)acceleration);
free((char *)label);

各ポインターは、対応する(および一致する)タイプのものです。これを行う意味はまったくありません。これは非常に古いコードなので、K&Rのものかどうか疑問に思っています。もしそうなら、私は実際にこれを必要とするかもしれない古いコンパイラをサポートしたいので、それらを削除したくありません。

これらのキャストを使用する技術的な理由はありますか?私はそれらを使用する実用的な理由の多くを見ない。データ型を解放する直前にデータ型を思い出すことのポイントは何ですか?

編集:この質問はnot他の質問の複製です。もう1つの質問は、この質問の特別なケースです。親しい投票者がすべての回答を読んだ場合、それは明らかだと思います。

Colophon:「定数回答」にチェックマークを付けています。これは、これを行う必要がある真の理由であるためです。ただし、ANSI C以前のカスタム(少なくとも一部のプログラマーの間)であるという答えが、私の場合に使用された理由のようです。ここの多くの人々による多くの良い点。ご協力ありがとうございます。

164

ポインターがconstの場合、コンパイラーの警告を解決するためにキャストが必要になる場合があります。 freeの引数をキャストせずに警告を発生させるコードの例を次に示します。

const float* velocity = malloc(2*sizeof(float));
free(velocity);

そして、コンパイラ(gcc 4.8.3)は次のように述べています:

main.c: In function ‘main’:
main.c:9:5: warning: passing argument 1 of ‘free’ discards ‘const’ qualifier from pointer target type [enabled by default]
     free(velocity);
     ^
In file included from main.c:2:0:
/usr/include/stdlib.h:482:13: note: expected ‘void *’ but argument is of type ‘const float *’
 extern void free (void *__ptr) __THROW;

free((float*) velocity);を使用すると、コンパイラはエラーを停止します。

169

先行標準Cにはvoid*がなく、char*しかなかったため、渡されたすべてのパラメーターをキャストする必要がありました。したがって、古代のCコードに出会った場合、そのようなキャストが見つかる可能性があります。

参照のある同様の質問

最初のC標準がリリースされたとき、mallocおよびfreeのプロトタイプは、char*から現在のvoid*に変更されました。

そしてもちろん、標準Cでは、そのようなキャストは不要であり、読みやすさを損ないます。

60
Lundin

以下に、キャストなしでfreeが失敗する例を示します。

volatile int* p = (volatile int*)malloc(5 * sizeof(int));
free(p);        // fail: warning C4090: 'function' : different 'volatile' qualifiers
free((int*)p);  // success :)
free((void*)p); // success :)

Cでは、警告を受け取ることができます(VS2012で警告があります)。 C++ではエラーが発生します。

まれなケースはさておき、キャストはコードを膨張させるだけです...

編集:void*ではなくint*にキャストして、失敗をデモしました。 int*が暗黙的にvoid*に変換されるのと同じように機能します。 int*コードを追加しました。

33
egur

古い理由:1. free((sometype*) ptr)を使用することにより、コードは、ポインターがfree()呼び出しの一部として考慮されるべきタイプを明示します。明示的なキャストは、free()が(do-it-your-self)DIY_free()に置き換えられる場合に役立ちます。

#define free(ptr) DIY_free(ptr, sizeof (*ptr))

DIY_free()は、特にデバッグモードで、解放されているポインターのランタイム分析を行う方法でした。多くの場合、これはDIY_malloc()とペアになって、センテンス、グローバルメモリ使用数などを追加します。私のグループは、より新しいツールが登場するまで何年もこの手法を使用していました。それは、解放されたアイテムが元々割り当てられたタイプにキャストされることを義務付けていました。

  1. メモリの問題を追跡するのに何時間も費やしたことを考えると、型を解放するなどのちょっとしたトリックは、デバッグの検索と絞り込みに役立ちます。

モダン: Manos Nikolaidis @ および @ egur で対処されているconstおよびvolatile警告の回避。 3つの修飾子の効果に注意すると思います:constvolatile、およびrestrict

[編集] char * restrict *rp2 per @ R ..を追加しました。comment

void free_test(const char *cp, volatile char *vp, char * restrict rp, 
    char * restrict *rp2) {
  free(cp);  // warning
  free(vp);  // warning
  free(rp);  // OK
  free(rp2);  // warning
}

int main(void) {
  free_test(0,0,0,0);
  return 0;
}
30
chux

別の対立仮説があります。

プログラムはC89以前に作成されたと言われています。つまり、freeのようなものがなかっただけでなく、constのプロトタイプと何らかの不一致を回避することはできません。 void * C89以前は、C89以前の関数プロトタイプのようなものはありませんでした。 stdlib.h自体が委員会の発明でした。システムヘッダーがfreeを宣言することに煩わされた場合、次のようになります。

extern free();  /* no `void` return type either! */

さて、ここで重要なのは、関数プロトタイプが存在しないということは、コンパイラが引数の型チェックなしを行ったことを意味するということです。デフォルトの引数プロモーション(変数の関数呼び出しに適用されるものと同じもの)を適用しました。各呼び出しサイトで引数を作成する責任は、呼び出し先の期待に沿って並んでいます全体プログラマー。

ただし、これは、ほとんどのK&Rコンパイラでfreeに引数をキャストする必要があったことを意味しません。のような関数

free_stuff(a, b, c)
    float *a;
    char *b;
    int *c;
{
    free(a);
    free(b);
    free(c);
}

正しくコンパイルされているはずです。だから私たちがここで手に入れたのは、異常な環境のバグのあるコンパイラに対処するために書かれたプログラムだと思います。たとえば、sizeof(float *) > sizeof(int)とコンパイラwould n'tが呼び出しの時点でそれらをキャストしない限り、ポインターの適切な呼び出し規則。

私はそのような環境については知りませんが、それがなかったという意味ではありません。思い浮かぶ最も可能性の高い候補は、1980年代初頭の8ビットおよび16ビットマイクロ用の「小さなC」コンパイラーです。また、初期のクレイがこのような問題を抱えていたことを知っても驚かないでしょう。

16
zwol

freeは、パラメーターとして非constポインターのみを取ります。したがって、constポインターの場合、非constポインターへの明示的なキャストが必要です。

Cでconstポインターを解放できない

9
Nobody