16

このスレッドでの回答に対してコメントを受け取りました。

関数呼び出し内の Malloc は、戻り時に解放されているように見えますか?

要するに、次のようなコードがありました。

int * somefunc (void)
{
  int * temp = (int*) malloc (sizeof (int));
  temp[0] = 0;
  return temp;
}

こんなコメントを頂きました。

malloc の戻り値をキャストしないでください。これは必須ではなく、エラーを隠すことができます。

C ではキャストが必要ないことに同意します。C++ では必須なので、ある日 C++ にコードを移植する必要がある場合に備えて、通常はキャストを追加します。

しかし、このようなキャストがどのようにエラーを隠すことができるのだろうか。何か案は?

編集:

双方に非常に適切で有効な議論があるようです。投稿していただきありがとうございます。

4

12 に答える 12

17

コメントを残したので、回答を投稿するのが適切なようです:P

基本的に、インクルードを忘れるとstdlib.h、コンパイラmallocint. キャストしないと、警告が表示されます。キャストを使用すると、そうはなりません。

したがって、キャストしても何も得られず、正当な警告が抑制されるリスクがあります。

これについては多くのことが書かれています。グーグルで検索すると、より詳細な説明が表示されます。

編集

と主張されてきた

TYPE * p;
p = (TYPE *)malloc(n*sizeof(TYPE));

pたとえば、 ではTYPeないと思ったために誤って十分なメモリを割り当てなかった場合は、それが明らかになります。TYPEしたがって、 malloc をキャストする必要があります。

私は2つのことを指摘したいと思います:

  1. p = malloc(sizeof(*p)*n);常に適切な量のスペースをmallocするように書く必要があります
  2. p上記のアプローチでは、 の型を変更する場合、宣言で 1 回、mallocで 1 回、キャストで 1 回の 3 か所で変更を加える必要があります。

要するに、私はまだ個人的に、戻り値をキャストする必要はないと信じておりmalloc、それは確かにベストプラクティスではありません.

于 2008-09-20T17:17:51.077 に答える
11

この質問は C と C++ の両方でタグ付けされているため、少なくとも 2 つの回答があります。

C

ええと...好きなことをしてください。

上記の「「stdlib」を含めないと警告が表示されない」という理由は有効ではないと思います。ヘッダーを含めることを忘れないように、この種のハックに頼るべきではないからです。

キャストを書かない本当の理由は、C コンパイラがすでに avoid *をあなたが望むポインタ型に暗黙のうちにキャストしているためです。

タイプ セーフが必要な場合は、C++ に切り替えるか、次のような独自のラッパー関数を記述できます。

int * malloc_Int(size_t p_iSize) /* number of ints wanted */
{
   return malloc(sizeof(int) * p_iSize) ;
}

C++

場合によっては、C++ でも、malloc/realloc/free ユーティリティを活用する必要があります。次に、キャストする必要があります。しかし、あなたはすでにそれを知っていました。static_cast<>() を使用することは、いつものように、C スタイルのキャストよりも優れています。

また、C では、テンプレートを介して malloc (および realloc など) をオーバーライドして、タイプ セーフを実現できます。

template <typename T>
T * myMalloc(const size_t p_iSize)
{
 return static_cast<T *>(malloc(sizeof(T) * p_iSize)) ;
}

次のように使用されます。

int * p = myMalloc<int>(25) ;
free(p) ;

MyStruct * p2 = myMalloc<MyStruct>(12) ;
free(p2) ;

および次のコード:

// error: cannot convert ‘int*’ to ‘short int*’ in initialization
short * p = myMalloc<int>(25) ;
free(p) ;

コンパイルされないので、問題ありません

全体として、純粋な C++ では、誰かがコード内に複数の C malloc を見つけたとしても言い訳はできません... :-)

C + C++ クロスオーバー

場合によっては、C と C++ の両方でコンパイルされるコードを生成したいことがあります (何らかの理由で... それが C++extern "C" {}ブロックのポイントではありませんか?)。この場合、C++ はキャストを要求しますが、C は static_cast キーワードを理解しないため、解決策は C スタイルのキャストです (これはまさにこの種の理由で C++ では依然として有効です)。

純粋な C コードを記述しても、C++ コンパイラでコンパイルすると、より多くの警告とエラーが発生することに注意してください (たとえば、上記のエラーとは異なり、最初に宣言せずに関数を使用しようとするとコンパイルされません)。

したがって、安全のために、C++ で問題なくコンパイルできるコードを作成し、警告を調べて修正し、C コンパイラを使用して最終的なバイナリを生成します。これは、キャストを C スタイルのキャストで記述することを意味します。

于 2008-09-20T18:55:22.683 に答える
7

発生する可能性のあるエラーの1つは、C(C ++ではなく)を使用して64ビットシステムでコンパイルしている場合です。

基本的に、を含めるのを忘れた場合stdlib.h、デフォルトのintルールが適用されます。mallocしたがって、コンパイラは、On Many 64ビットシステムのプロトタイプがありint malloc();、intが32ビットで、ポインタが64ビットであると喜んで想定します。

ええと、値は切り捨てられ、ポインタの下位32ビットしか取得できません。ここで、の戻り値をキャストするとmalloc、このエラーはキャストによって隠されます。ただし、そうしないと、エラーが発生します(「intをT *に変換できません」という性質の何か)。

もちろん、これは2つの理由からC++には適用されません。まず、デフォルトのintルールがなく、次にキャストが必要です。

とにかく、全体として、C++コードを新しくする必要があります:-P。

于 2008-09-20T18:47:15.073 に答える
6

まあ、それは正反対だと思います-常に必要なタイプに直接キャストします。ここで読んでください!

于 2008-09-20T17:19:16.003 に答える
2

「stdlib.h を忘れた」という引数はストローマンです。最新のコンパイラは、問題を検出して警告します (gcc -Wall)。

malloc の結果は常にすぐにキャストする必要があります。そうしないことは、C++ として失敗するという理由だけでなく、エラーと見なされるべきです。たとえば、さまざまな種類のポインターを備えたマシン アーキテクチャをターゲットにしている場合、キャストを入れないと、非常に厄介なバグが発生する可能性があります。

編集:コメンターのエヴァン・テランは正しいです。私の間違いは、コンパイラがどのコンテキストでも void ポインタに対して何もする必要がないと考えていたことです。私は FAR ポインターのバグを考えるとびっくりするので、私の直感はすべてをキャストすることです。ありがとうエヴァン!

于 2008-09-20T18:58:44.610 に答える
1

人々は、私がいつも小出しにする理由をすでに挙げています:後の更新に関係なく、型とサイズが常に一致することを確認するために、含めstdlib.hたり使用したりしないという古い (ほとんどのコンパイラには適用できなくなった) 議論です。sizeof *pキャスティングに対するもう 1 つの議論を指摘したいと思います。小さいですが、当てはまると思います。

C はかなり弱い型付けです。ほとんどの安全な型変換は自動的に行われ、安全でない型変換のほとんどはキャストが必要です。検討:

int from_f(float f)
{
    return *(int *)&f;
}

危険なコードです。技術的には未定義の動作ですが、実際には、実行するほぼすべてのプラットフォームで同じことを行います。そしてキャストは、「このコードはひどいハックだ」と教えてくれます。

検討:

int *p = (int *)malloc(sizeof(int) * 10);

キャストを見て、「なぜこれが必要なのか?ハックはどこにあるのか?」と疑問に思います。実際にはコードが完全に無害であるのに、何か悪いことが起こっているのではないかと首に毛が生えます。

C を使用している限り、キャスト (特にポインター キャスト) は、「何か邪悪で簡単に壊れる可能性がある」ことを示す方法です。彼らはあなたが達成する必要があることを達成するかもしれませんが、あなたと将来のメンテナーに子供たちが大丈夫ではないことを示しています.

every でキャストを使用するmallocと、ポインター キャストの「ハック」の兆候が減少します。のようなものを見ると不快感が少なくなります*(int *)&f;

注: C と C++ は異なる言語です。C は弱く型付けされていますが、C++ はより強く型付けされています。(私の謙虚な意見では) 不必要に強力な C++ 型システムのため、キャストC++ で必要です。ハックをまったく示していません。(実際、この特定のケースは、C++ 型システムが「強すぎる」と私が考える唯一の場所ですが、「弱すぎる」場所は考えられず、全体的に私の好みには強すぎます。)

C++ の互換性について心配している場合は、心配しないでください。C で記述している場合は、C コンパイラを使用します。すべてのプラットフォームで利用できる本当に優れたものがたくさんあります。なんらかの非常識な理由で、 C++ としてクリーンにコンパイルされる C コードを作成する必要ある場合、実際には C を作成していません。C を C++ に移植する必要がある場合は、C コードをより慣用的にするために多くの変更を行う必要があります。 C++.

それができない場合は、何をしてもコードが美しくないため、その時点でどのようにキャストするかは問題ではありません。テンプレートを使用して正しい型を返す新しいアロケーターを作成するというアイデアは気に入っていますが、それは基本的にnewキーワードを再発明するだけです。

于 2010-02-10T18:31:23.587 に答える
1

実際、キャストがエラーを隠すことができる唯一の方法は、あるデータ型からより小さなデータ型に変換してデータを失った場合、またはナシをリンゴに変換していた場合です。次の例を見てください。

int int_array[10];
/* initialize array */
int *p = &(int_array[3]);
short *sp = (short *)p;
short my_val = *sp;

この場合、short への変換は int から一部のデータを削除します。そして、このケース:

struct {
    /* something */
} my_struct[100];

int my_int_array[100];
/* initialize array */
struct my_struct *p = &(my_int_array[99]);

間違った種類のデータを指したり、無効なメモリを指すことさえあります。

しかし、一般的に、自分が何をしているのかを知っていれば、キャスティングを行っても問題ありません。malloc からメモリを取得している場合はなおさらです。これは、キャストしない限りまったく使用できない void ポインターを返します。ほとんどのコンパイラは、左辺値 (割り当ての左側)とにかく取ることができません。

于 2008-09-20T17:25:26.690 に答える
1
#if CPLUSPLUS
#define MALLOC_CAST(T) (T)
#else
#define MALLOC_CAST(T)
#endif
...
int * p;
p = MALLOC_CAST(int *) malloc(sizeof(int) * n);

または、代わりに

#if CPLUSPLUS
#define MYMALLOC(T, N) static_cast<T*>(malloc(sizeof(T) * N))
#else
#define MYMALLOC(T, N) malloc(sizeof(T) * N)
#endif
...
int * p;
p = MYMALLOC(int, n);
于 2008-09-28T02:40:16.707 に答える
0

(void *) を返す関数を (int *) にキャストしても害はありません。ある型のポインターを別の型にキャストしています。

代わりに整数を返す関数をポインターにキャストすることは、おそらく正しくありません。明示的にキャストしなければ、コンパイラはフラグを立てていたでしょう。

于 2008-09-20T17:17:06.770 に答える
0

考えられるエラーの 1 つは (本当に必要かどうかによって異なります)、1 つのサイズ スケールで malloc し、別の型のポインターに代入することです。例えば、

int *temp = (int *)malloc(sizeof(double));

そうしたい場合もあるかもしれませんが、稀だと思います。

于 2008-09-20T17:19:08.280 に答える
0

キャストを入れるべきだと思います。型には 3 つの場所があると考えてください。

T1 *p;
p = (T2*) malloc(sizeof(T3));

2 行のコードは大きく分かれている可能性があります。したがって、コンパイラがT1 == T2を強制するのは良いことです。T2 == T3 であることを視覚的に確認する方が簡単です。

T2 キャストを逃した場合、T1 == T3 であることを期待する必要があります。

一方、 stdlib.h 引数がありませんが、問題になる可能性は低いと思います。

于 2008-09-20T17:41:26.197 に答える
-1

一方、コードを C++ に移植する必要がある場合は、'new' 演算子を使用する方がはるかに優れています。

于 2008-09-20T17:21:46.137 に答える