この質問では、 の結果をキャストすべきではないというコメントが誰かから提案されました。つまり、私はこれを行う必要があります:malloc
int *sieve = malloc(sizeof(*sieve) * length);
それよりも:
int *sieve = (int *) malloc(sizeof(*sieve) * length);
なぜこれが当てはまるのでしょうか?
int *sieve = (int *) malloc(sizeof(int) * length);
には 2 つの問題があります。キャストと、変数の代わりに型を sizeof の引数として使用していること。代わりに、次のようにします。
int *sieve = malloc(sizeof *sieve * length);
いいえ; 次の理由から、結果をキャストしません。
void *
この場合、他のポインター型に自動的かつ安全に昇格されるため、不要です。<stdlib.h>
。これにより、クラッシュが発生する可能性があります (または、さらに悪いことに、コードのまったく異なる部分でかなり後になるまでクラッシュが発生しません)。ポインターと整数のサイズが異なるとどうなるかを考えてみましょう。キャストによって警告を隠しているため、返されたアドレスの一部が失われる可能性があります。注: C99 以降、暗黙の関数は C から削除されました。宣言されていない関数が を返すという自動的な仮定がないため、この点はもはや関係ありませんint
。明確にするために、「キャストする必要はない」ではなく、「キャストしない」と言ったことに注意してください。私の意見では、たとえ正しく理解できたとしても、キャストを含めるのは失敗です。それを行うメリットはまったくありませんが、多くの潜在的なリスクがあり、キャストを含めることは、リスクについて知らないことを示しています.
また、コメンテーターが指摘しているように、上記は C++ ではなくストレート C について述べていることに注意してください。私は、C と C++ は別々の言語であると固く信じています。
さらに追加すると、コードは型情報 ( int
) を不必要に繰り返し、エラーを引き起こす可能性があります。戻り値を格納するために使用されているポインターを逆参照して、2 つを一緒に "ロック" することをお勧めします。
int *sieve = malloc(length * sizeof *sieve);
これlength
により、視認性を高めるために も前面に移動し、冗長な括弧をsizeof
;で削除します。引数が型名の場合にのみ必要です。多くの人はこれを知らない (または無視している) ようで、コードが冗長になります。覚えておいてください:sizeof
は関数ではありません! :)
length
まれに前面に移動することで視認性が向上する場合もありますが、一般的な場合は次のように記述した方がよいことにも注意が必要です。
int *sieve = malloc(sizeof *sieve * length);
この場合、最初のものを保持するsizeof
ことで、少なくともsize_t
数学で乗算が行われることが保証されます。
比較:malloc(sizeof *sieve * length * width)
対。malloc(length * width * sizeof *sieve)
_ length * width
_ width
_length
size_t
C では、 の戻り値をキャストする必要はありませんmalloc
。によって返される void へのポインターmalloc
は、正しい型に自動的に変換されます。ただし、コードを C++ コンパイラでコンパイルする場合は、キャストが必要です。コミュニティの間で推奨される代替手段は、次を使用することです。
int *sieve = malloc(sizeof *sieve * length);
これにより、 の型を変更した場合でも、式の右辺を変更することを心配する必要がなくなりますsieve
。
人々が指摘したように、キャストは悪いです。特にポインターキャスト。
You do cast, because:
type *
versus type **
.#include
an appropriate header file misses the forest for the trees. It's the same as saying "don't worry about the fact you failed to ask the compiler to complain about not seeing prototypes -- that pesky stdlib.h is the REAL important thing to remember!"malloc()
bugs are caught much faster when there's a cast. As with assertions, annotations that reveal intent decrease bugs. void
C では、ポインターを他の種類のポインターに暗黙的に変換できるため、キャストは必要ありません。これを使用すると、何気ない観察者に、それが必要な何らかの理由があることを示唆する可能性があり、誤解を招く可能性があります。
の結果をキャストしません。キャストするmalloc
と、コードが無意味に混乱するからです。
人々が結果をキャストする最も一般的な理由malloc
は、C 言語がどのように機能するかがわからないためです。これは警告サインです。特定の言語メカニズムがどのように機能するかがわからない場合は、推測しないでください。調べるか、Stack Overflow で質問してください。
いくつかのコメント:
void ポインターは、明示的なキャストなしで他のポインター型との間で変換できます (C11 6.3.2.3 および 6.5.16.1)。
void*
ただし、C++ では、と別のポインター型の間の暗黙的なキャストは許可されません。したがって、C++ では、キャストは正しいはずです。ただし、C++ でプログラミングする場合は、and を使用new
しないでmalloc()
ください。また、C++ コンパイラを使用して C コードをコンパイルしないでください。
同じソース コードで C と C++ の両方をサポートする必要がある場合は、コンパイラ スイッチを使用して違いをマークします。互換性がないため、両方の言語標準を同じコードで満たそうとしないでください。
ヘッダーをインクルードするのを忘れたために C コンパイラが関数を見つけられない場合、それに関するコンパイラ/リンカー エラーが発生します。したがって、<stdlib.h>
大したことではないことを含めるのを忘れた場合、プログラムを構築することはできません。
25 年以上前のバージョンの標準に準拠する古いコンパイラでは、インクルード<stdlib.h>
を忘れると危険な動作が発生します。その古い標準では、目に見えるプロトタイプのない関数は、暗黙的に戻り値の型を に変換したためint
です。結果を明示的にキャストすると、malloc
このバグが隠されます。
しかし、それは本当に問題ではありません。25 年前のコンピューターを使用していないのに、なぜ 25 年前のコンパイラーを使用するのでしょうか?
C では、 からvoid *
他の (データ) ポインターへの暗黙的な変換が行われます。
によって返される値をキャストするmalloc()
必要はありませんが、誰も指摘していないように思われる点を 1 つ追加したいと思います。
昔、つまりANSI Cvoid *
がポインタのジェネリック型として を提供する前は、char *
がそのような使用法のための型でした。その場合、キャストはコンパイラの警告をシャットダウンできます。
参考:C FAQ
私の経験を追加して、コンピューター工学を勉強している私は、C で書いているのを見た 2 人か 3 人の教授が常に malloc をキャストしていることがわかりますが、私が尋ねた人 (膨大な履歴書と C の理解を持って) は、それは絶対に不要であると私に言いましたが、絶対に具体的であり、学生を絶対に具体的であるという考え方に導くためだけに使用されていました。基本的に、キャストによって動作が変わることはありません。まさにその通りに実行され、メモリが割り当てられます。キャストしても影響はありません。同じメモリが得られます。誤って別のものにキャストしたとしても (そして何らかの形でコンパイラを回避したとしても)エラー) C は同じ方法でアクセスします。
編集:キャスティングには特定のポイントがあります。配列表記を使用する場合、生成されたコードは、次の要素の先頭に到達するために何個のメモリ位置を進めなければならないかを知る必要があります。これは、キャストによって実現されます。このようにして、double の場合は 8 バイト進み、int の場合は 4 バイト進む、などがわかります。したがって、ポインタ表記を使用しても効果がなく、配列表記では必要になります。
malloc
を返すため、 の結果をキャストすることは必須ではなくvoid*
、 avoid*
は任意のデータ型を指すことができます。
これは、GNU C ライブラリ リファレンスマニュアルに記載されている内容です。
ISO C は必要に応じて型を別の型のポインターに
malloc
自動的に変換するため、キャストなしで任意のポインター変数に結果を格納できます。void *
ただし、代入演算子以外のコンテキストでは、またはコードを従来の C で実行する場合は、キャストが必要です。
実際、ISO C11 標準(p347) には次のように書かれています。
割り当てが成功した場合に返されるポインターは、基本的なアライメント要件を持つ任意の型のオブジェクトへのポインターに割り当てられるように適切にアライメントされ、割り当てられた空間内のそのようなオブジェクトまたはそのようなオブジェクトの配列にアクセスするために使用されます (スペースは明示的に割り当て解除されます)
void ポインターは汎用オブジェクト ポインターであり、C は void ポインター型から他の型への暗黙的な変換をサポートしているため、明示的に型キャストする必要はありません。
ただし、暗黙的な変換をサポートしていない C++ プラットフォームで同じコードを完全に互換性を持たせて動作させたい場合は、型キャストを行う必要があるため、すべて使いやすさに依存します。
返される型は void* であり、逆参照可能にするために目的の型のデータ ポインターにキャストできます。
プログラミング言語とコンパイラに依存します。C で使用する場合malloc
は、自動的に型キャストされるため、型キャストする必要はありません。ただし、C++ を使用している場合は、型malloc
を返すため、型キャストする必要がありますvoid*
。
C 言語では、void ポインターを任意のポインターに割り当てることができるため、型キャストを使用しないでください。「タイプ セーフ」な割り当てが必要な場合は、C プロジェクトで常に使用する次のマクロ関数をお勧めします。
#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)
これらが整っていれば、簡単に言うことができます
NEW_ARRAY(sieve, length);
非動的配列の場合、3 番目に必要な関数マクロは次のとおりです。
#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
これにより、配列ループがより安全で便利になります。
int i, a[100];
for (i = 0; i < LEN(a); i++) {
...
}
GCC と Clang に慣れている人は甘やかされてしまいます。それほど良いことばかりではありません。
私は何年もの間、私が使用しなければならなかった驚くほど古いコンパイラーにかなり恐怖を感じてきました。多くの場合、企業や管理者はコンパイラーの変更に対して非常に保守的なアプローチを採用し、新しいコンパイラー (標準への準拠とコードの最適化が改善されたもの) が自社のシステムで機能するかどうかをテストすることさえしません。作業中の開発者にとっての実際の現実は、コーディング中にベースをカバーする必要があるということです。残念ながら、コードに適用されるコンパイラを制御できない場合は、malloc をキャストすることをお勧めします。
また、多くの組織が独自のコーディング標準を適用し、それが定義されている場合はそれに従う方法を人々が採用することをお勧めします。明示的なガイダンスがない場合、私は標準への奴隷的な順守よりも、どこでもコンパイルできる可能性が最も高い傾向にあります。
現在の基準では必要ないという議論は非常に有効です。しかし、その議論は現実世界の実用性を省いています。私たちは、当時の標準だけに支配されている世界ではなく、私が「ローカル マネジメントの現実領域」と呼んでいる実用性に支配されている世界でコーディングを行っています。そして、それは時空がかつてないほど曲がってねじれています。:-)
YMMV。
私は malloc をキャストすることを防御操作と考える傾向があります。きれいでも完璧でもありませんが、一般的に安全です。(正直なところ、 stdlib.h を含めていない場合は、malloc をキャストするよりもはるかに多くの問題が発生します!)。
時々、次のようなコメントに気づきます。
また
OPがキャストを使用する質問について。コメント自体には、この質問へのハイパーリンクが含まれています。
それは、あらゆる意味で不適切であり、不正確でもあります。それが本当に自分自身のコーディングスタイルの問題である場合、正しいことも間違っていることもありません。
それは次の 2 つの理由に基づいています。
この質問は確かに意見に基づいています。技術的には、質問は数年前に意見に基づくものとしてクローズされるべきでした。「すべきか」または「すべきでないか」または同等の「すべきか」または「すべきではないか」という質問では、自分の意見の態度がなければ、集中して答えることができません。ここでよく示されているように、質問を閉じる理由の 1 つは、「意見に基づく回答につながる可能性がある」ためです。
多くの回答 ( @unwindの最も明白で受け入れられている回答を含む) は、完全にまたはほぼ完全に意見に基づいています (キャストを行ったり、自分自身を繰り返したりする場合にコードに追加される不思議な「混乱」は悪いことです)。キャストを省略する明確で集中的な傾向。彼らは片側のキャストの冗長性について議論していますが、さらに悪いことに、プログラミング自体のバグ/失敗によって引き起こされたバグを解決することについても議論しています-使用したい場合はそうではありません.#include <stdlib.h>
malloc()
議論されたいくつかの点について、私の個人的な意見は控えて、本当の見方を示したいと思います。特にいくつかの点に注意する必要があります。
自分の意見に陥りやすいこのような質問には、中立的な賛否両論の答えが必要です。短所や長所だけではありません。
長所と短所の概要は、この回答に記載されています。
https://stackoverflow.com/a/33047365/12139179
(これまでのところ、これが最良の答えであると個人的に考えています。)
キャストを省略した理由のほとんどは、キャストがバグを隠している可能性があることです。
この質問に示されているように、誰かがmalloc()
返す暗黙の宣言を使用している場合int
(暗黙の関数はC99以降標準から削除されています)およびsizeof(int) != sizeof(int*)
このコードが 64 ビット アーキテクチャでは segfault になるのに、32 ビットでは問題なく動作するのはなぜですか?
キャストはバグを隠します。
これは事実ですが、キャストの省略は、さらに大きなバグに対する前向きな解決策に過ぎないため、ストーリーの半分しか示していませstdlib.h
んmalloc()
。
これは決して深刻な問題ではありません.
C99 以上に準拠したコンパイラを使用する (これは推奨されており、必須である必要があります)。
stdlib.h
コードで使用したいときに ,を含めるのを忘れるほど欠席することはありませんmalloc()
。これはそれ自体が大きなバグです。
C++ ではキャストが義務付けられているため、C コードの C++ 準拠について議論する人もいます。
まず、一般的に言うと、C++ コンパイラを使用して C コードをコンパイルすることは、適切な方法ではありません。
実際、C と C++ は、セマンティクスが異なる 2 つの完全に異なる言語です。
ただし、C コードを C++ に準拠させたい、またはその必要がある場合、またはその逆の場合は、キャストの代わりにコンパイラ スイッチを使用します。
キャストは冗長または有害でさえあると宣言される傾向があるため、キャストが有用または必要でさえある正当な理由を示すこれらの質問に焦点を当てたいと思います。
実際には、割り当てられたポインターが基本的なアラインメント要件のオブジェクト (ほとんどのオブジェクトを含む) を指している場合、キャストは C 標準 (ANSI-C (C89/C90) 以降) に従って冗長です。
この場合、ポインターは自動的に整列されるため、キャストを行う必要はありません。
「aligned_alloc、calloc、malloc、および realloc 関数の連続呼び出しによって割り当てられるストレージの順序と連続性は指定されていません。割り当てが成功した場合に返されるポインターは、任意の型のオブジェクトへのポインターに割り当てられるように適切に整列されます。基本的なアライメント要件であり、割り当てられたスペース内のそのようなオブジェクトまたはそのようなオブジェクトの配列にアクセスするために使用されます (スペースが明示的に割り当て解除されるまで)。」
出典: C18、§7.22.3/1
「基本的なアラインメントは、以下の有効なアラインメントです
_Alignof (max_align_t)
。基本的なアラインメントは、すべてのストレージ期間のオブジェクトの実装によってサポートされます。次のタイプのアラインメント要件は、基本的なアラインメントです。— すべてのアトミック、修飾、または非修飾の基本型。
— すべてのアトミック、修飾、または非修飾の列挙型。
— すべてのアトミック、修飾、または非修飾ポインター型。
— 要素型に基本的なアラインメント要件があるすべての配列型; 57)
- 完全なオブジェクトタイプとして箇条 7 で指定されたすべてのタイプ。
— すべての要素が基本アラインメント要件を持つ型を持ち、基本アラインメントではないアラインメントを指定するアラインメント指定子を要素に持たないすべての構造体型または共用体型。
- 6.2.1 で指定されているように、後の宣言は前の宣言を隠す可能性があります。」
出典: C18、§6.2.8/2
ただし、拡張アラインメント要件の実装定義オブジェクトにメモリを割り当てる場合は、キャストが必要になります。
拡張アラインメントは、 より大きいアラインメントで表され
_Alignof (max_align_t)
ます。拡張アラインメントがサポートされているかどうか、およびそれらがサポートされているストレージ期間は実装定義です。拡張アラインメント要件を持つタイプは、オーバー アライン タイプです。ソース。C18、§6.2.8/3
他のすべては、特定のユースケースと自分の意見の問題です。
教育の仕方には気をつけてください。
最初にこれまでに行われたすべての回答を注意深く読み(失敗を指摘する可能性のあるコメントも) malloc()
、特定のケースでの結果をキャストする場合、またはキャストしない場合は、独自の意見を構築することをお勧めします。
ご注意ください:
その質問には正解も不正解もありません。それはスタイルの問題であり、どちらを選択するかはあなた自身が決めることです (もちろん、教育や仕事によって強制されない場合)。だまされないように注意してください。
最後のメモ: 私は最近、この質問を意見に基づくものとして閉じることに投票しました。閉鎖/再開の特権をお持ちでしたら、ぜひそうしていただきたいと思います。
私がキャストを挿入したのは、型システムの醜い穴が承認されていないことを示すためだけです。これにより、次のスニペットなどのコードを診断せずにコンパイルできますが、不適切な変換を引き起こすためにキャストが使用されていません。
double d;
void *p = &d;
int *q = p;
それが存在しないことを望みます (そして、C++ には存在しません) ので、キャストします。それは私の好みと私のプログラミング方針を表しています。私はポインターをキャストするだけでなく、効果的に投票を行い、愚かな悪魔を追い出します. 私が実際に 愚かさを追い出すことができない場合は、少なくとも抗議のジェスチャーでそうしたいという願望を表明させてください.
実際には、malloc
を返す関数でラップ (および友人)をラップunsigned char *
し、基本的にvoid *
コードで使用しないことをお勧めします。任意のオブジェクトへの一般的なポインターが必要な場合は、char *
またはunsigned char *
を使用し、両方向にキャストします。おそらく、満足できる唯一のリラクゼーションは、キャストなしのような関数を使用するmemset
ことmemcpy
です。
キャストと C++ の互換性については、C と C++ の両方としてコンパイルされるようにコードを記述した場合 (この場合、以外のものに代入するときにの戻り値をキャストする必要があります)、非常に役立つことができます。 C++ としてコンパイルする場合は C++ スタイルのキャストに変換されますが、C としてコンパイルする場合は C キャストに変換されます。malloc
void *
/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif
これらのマクロに準拠している場合grep
は、コード ベースでこれらの識別子を検索するだけで、すべてのキャストがどこにあるかが表示されるため、それらのいずれかが正しくないかどうかを確認できます。
その後、定期的に C++ でコードをコンパイルすると、適切なキャストの使用が強制されます。たとえば、strip_qual
単にconst
orを削除するために を使用しvolatile
ているが、型変換が含まれるようにプログラムが変更された場合、診断が表示され、目的の変換を得るためにキャストの組み合わせを使用する必要があります。
これらのマクロを順守するのに役立つように、GNU C++ (C ではありません!) コンパイラには美しい機能があります。それは、C スタイルのキャストのすべての発生に対して生成されるオプションの診断です。
-Wold-style-cast (C++ および Objective-C++ のみ) 非 void 型への古いスタイル (C スタイル) のキャストが使用されている場合に警告する C++ プログラム内。新しいスタイルのキャスト (dynamic_cast、 static_cast、reinterpret_cast、const_cast) は脆弱性が低い 意図しない効果が得られ、検索がはるかに簡単になります。
C コードが C++ としてコンパイルされる場合、このオプションを使用して、コードに忍び込む可能性-Wold-style-cast
のあるキャスト構文のすべての出現箇所を見つけ、上記のマクロ (または(type)
必要に応じて組み合わせる)。
この変換の扱いは、「クリーン C」で作業するための単一の最大のスタンドアロンの技術的正当化です: C と C++ の方言を組み合わせたものであり、これにより、 の戻り値をキャストすることが技術的に正当化されますmalloc
。
可能な限り C でプログラミングするときに行う最善の方法は次のとおりです。
-Wall
すべてのエラーと警告を修正します。auto
-Wall
ます-std=c++11
。すべてのエラーと警告を修正します。この手順により、C++ の厳密な型チェックを利用できるため、バグの数を減らすことができます。特に、この手順では、含めることを強制されますstdlib.h
。
malloc
このスコープ内で宣言されていませんでした
また、結果をキャストするように強制するか、malloc
または取得します
void*
からへの無効な変換T*
またはあなたのターゲットタイプが何であれ。
私が見つけたC++の代わりにCで書くことの唯一の利点は
C に共通のサブセットを静的ポリモーフィック機能と一緒に使用すると、理想的なケースでは 2 番目の短所がなくなることに注意してください。
C++ の厳密な規則が不便だと思う人のために、C++11 の機能を推論された型で使用できます。
auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
キャストは、C++ ではなく C++ のみを対象としています。C++ コンパイラを使用している場合は、C コンパイラに変更することをお勧めします。
void ポインターの背後にある概念は、malloc が void を返す理由である任意のデータ型にキャストできるということです。また、自動型キャストにも注意する必要があります。したがって、ポインターをキャストする必要はありますが、必須ではありません。コードをきれいに保ち、デバッグに役立ちます
他の人が述べたように、C では必要ありませんが、C++ では必要です。
キャストを含めると、C プログラムまたは関数を C++ としてコンパイルできる場合があります。
void * は自動的かつ安全に他のポインタ型に昇格されるため、C では不要です。
ただし、キャストすると、 stdlib.hを含めるのを忘れた場合にエラーが隠される可能性があり ます。これにより、クラッシュが発生する可能性があります (または、さらに悪いことに、コードのまったく別の部分でかなり後になるまでクラッシュが発生しません)。
stdlib.hに含まれているため、malloc のプロトタイプが見つかります。malloc のプロトタイプがない場合、標準では、C コンパイラが malloc が int を返すと想定する必要があります。キャストがない場合、この整数がポインターに割り当てられると警告が発行されます。ただし、キャストを使用すると、この警告は生成されず、バグが隠されます。
の主な問題は、適切なサイズmalloc
を取得することです。
返されるメモリの形式malloc()
はuntypedであり、単純なキャストにより魔法のように有効な型を取得することはありません。
どちらのアプローチも問題なく、どちらを選択するかはプログラマーの意図に依存すると思います。
ptr = (T*)malloc(sizeof(T));
ptr = malloc(sizeof *ptr);
最初のメソッドは、特定の型にメモリを割り当て、それをキャストして正しいポインタに割り当てられるようにすることで、正しいサイズを保証します。の型が正しくない場合ptr
、コンパイラは警告/エラーを発行します。の型ptr
が変更された場合、コンパイラはコードのリファクタリングが必要な場所を示します。
new
さらに、最初のメソッドは、C++ の operator に似たマクロに結合できます。
#define NEW(T) ((T*)malloc(sizeof(T)))
...
ptr = NEW(T);
さらに、このメソッドは の場合ptr
に機能しvoid*
ます。
2 番目のメソッドは型を気にせず、ポインターの型から取得することで正しいサイズを保証します。この方法の主な利点は、 のタイプptr
が変更されるたびにストレージ サイズが自動的に調整されることです。リファクタリング時の時間 (またはエラー) を節約できます。
不利な点は、そうである場合にこの方法が機能しないことですptr
がvoid*
、それは良いこととして認識される可能性があります。また、C++ では機能しないため、C++ プログラムで使用されるヘッダーのインライン関数では使用しないでください。
個人的には、2番目のオプションを好みます。
私にとって、ここでの持ち帰りと結論はmalloc
、Cでのキャストはまったく必要ないということですが、キャストしても、要求された祝福されたメモリスペースが引き続き割り当てられるため、malloc
影響はありません。malloc
もう1つの持ち帰りは、人々がキャストを行う理由または理由の1つであり、これは、CまたはC ++で同じプログラムをコンパイルできるようにすることです。
他にも理由があるかもしれませんが、遅かれ早かれ深刻なトラブルに巻き込まれることはほぼ確実です。