mallocの結果をキャストしますか?


2408

この質問、誰かがで提案されているコメント、私がすべきことではないの結果キャストmallocすなわち、

int *sieve = malloc(sizeof(int) * length);

のではなく:

int *sieve = (int *) malloc(sizeof(int) * length);

これはなぜでしょうか?


222
また、sieve = malloc(sizeof * sieve * length);と記述することで、より保守しやすくなります。
ウィリアムパーセル2009


3
ここでの答えは、一方的な地雷原です。答えは「場合によります」です。プログラムを機能させるために必要なことではありません。ただし、一部のコーディング標準では必要です...たとえば、CERT Cコーディング標準
Dr. Person Person II

奇妙なことに、あなたがキャストしないことに誰もが同意しますNULL。(それがおそらくC ++が導入された理由です。C++ではnullptr暗黙のポインターキャストが許可されていません)
Sapphire_Brick

できますが、必要はありません。ただし、C ++で行う必要があります。
user12211554

回答:


2217

いいえ ; 次の理由により、結果をキャストしません

  • void *この場合、自動的かつ安全に他のポインター型に昇格されるため、これは不要です。
  • それはコードに混乱を追加します、キャストは非常に読みにくいです(特にポインターの型が長い場合)。
  • それはあなたがあなた自身を繰り返すようにします、それは一般に悪いです。
  • 含めるのを忘れた場合、エラーを隠すことができます<stdlib.h>。これにより、クラッシュが発生する可能性があります(さらに悪いことに、コードの完全に異なる一部の部分まではクラッシュしません)。ポインタと整数のサイズが異なる場合にどうなるかを考えます。次に、キャストによって警告を非表示にし、返されたアドレスの一部を失う可能性があります。注:C99以降、暗黙的な関数はCからなくなり、宣言されていない関数がを返すという自動仮定がないため、この点は関係ありませんint

私は「あなたはキャストしていない」と述べたことを明確に、注意点としては、「あなたはしていないではない必要があるキャストに」。私の意見では、それが正しいとしても、キャストを含めるのは失敗です。それを行うだけの利点はありませんが、潜在的なリスクがたくさんあり、キャストを含めると、リスクについて知らないことがわかります。

また、コメンテーターが指摘するように、上記はC ++ではなくストレートCについて述べていることに注意してください。CとC ++は別々の言語であると私は強く信じています。

さらに追加すると、コードは不必要にタイプ情報(int)を繰り返し、エラーを引き起こす可能性があります。戻り値を格納するために使用されているポインターを逆参照して、2つを「ロック」することをお勧めします。

int *sieve = malloc(length * sizeof *sieve);

これはまた、length視認性を高めるためにを前に移動し、で余分な括弧を削除しsizeofます。これら、引数が型名である場合にのみ必要です。多くの人はこれを知らない(または無視する)ようで、コードがより冗長になります。覚えておいてください:sizeofは関数ではありません!:)


移動しながらlength前にするとまれでは視認性を高める、1はまた、一般的なケースでは、それはのように式を記述する方が良いでなければならないことに注意を払う必要があります。

int *sieve = malloc(sizeof *sieve * length);

sizeofこの場合、最初を維持するため、乗算は少なくともsize_t数学で行われます。

比較:malloc(sizeof *sieve * length * width)vs malloc(length * width * sizeof *sieve). 2番目はlength * widthwhen widthをオーバーフローする可能性があり、lengthより小さい型ですsize_t


21
回答の更新を検討してください。キャストはもはや危険ではなく、自分自身を繰り返すことは必ずしも悪いことではありません(冗長性はエラーの検出に役立ちます)。
n。「代名詞」m。

11
コンパイラが変更されました。最新のコンパイラは、mallocの宣言がないことを警告します。
n。「代名詞」m。

55
@nm OK ここを読んでいる誰もが特定のコンパイラを持っていると想定するのは悪いことだと思います。また、C11の「暗黙の関数」の概念全体がなくなったので、私はそれを知りませんでした。それでも、無意味なキャストを追加する意味はわかりません。あなたもint x = (int) 12;物事を明確にするために行いますか?
アンワインド

22
@nmは、ボイドポインターを明示的にキャストしてバグを「解決」した場合、未定義の動作に遭遇する可能性が高くなります。つまり、問題のプログラムには、まだ実行されていない、はるかに悪い未発見のバグがある可能性があります。そしてある日、寒い冬の夜に、仕事から家に帰ると、悪魔がユーザーの鼻から飛び出すことを訴える問題レポートが溢れているGitHubページが見つかります
Braden Best

12
@unwind私はあなたに同意しますが、(int)12比較することはできません。12 intキャストは何もしません。のretval malloc()void *、キャストされたポインター型ではありません。(そうでない場合void *、のアナロジーは誰も議論していない(int)12ことになります(void*)malloc(…)。)
Amin Negm-Awad 2017

376

Cでは、の戻り値をキャストする必要はありませんmalloc。が返すvoidへのポインタmallocは、自動的に正しい型に変換されます。ただし、コードをC ++コンパイラでコンパイルする場合は、キャストが必要です。コミュニティの間で推奨される代替策は、以下を使用することです。

int *sieve = malloc(sizeof *sieve * length);

これにより、のタイプを変更した場合でも、式の右側を変更する必要がなくなりますsieve

キャストが悪いのは、人々が指摘しているようです。特にポインターキャスト。


71
@MAKZ malloc(length * sizeof *sieve)それsizeofは変数のように見えるようにすることを主張するでしょう-だから私malloc(length * sizeof(*sieve))はもっと読みやすいと思います。
マイケルアンダーソン

21
そして、malloc(length * (sizeof *sieve))まだ、より読みやすいです。私見では。
Toby Speight

19
@Michael Andersonの()問題はさておき、提案されたスタイルが順序を入れ替えたことに注意してください。要素数がのようlength*widthに計算される場合を考慮し、sizeof最初の場合は最初の式を維持して、少なくともsize_t数学で乗算が行われることを保証します。比較malloc(sizeof( *ptr) * length * width)対を malloc(length * width * sizeof (*ptr))-第二がオーバーフローするlength*width際にwidth,lengthその小さいタイプがありますsize_t
chux-モニカ

3
@chuxそれは明白ではありませんが、私のコメントがあまり適切ではないように回答が編集されました-元の提案はmalloc(sizeof *sieve * length)
Michael Anderson

12
CはC ++ではありません。それらがふりをすることは、最終的に混乱と悲しみにつながります。C ++を使用している場合は、Cスタイルのキャストも不適切です(非常に古いC ++コンパイラを使用している場合を除く)。そしてstatic_cast>()(またはreinterpret_cast<>())Cのどの方言とも互換性がありません
David C.

349

あなたキャストするのは、

  • CとC ++の間でコードの移植性向上し、SOの経験が示すように、多くのプログラマーはC ++(またはCとローカルコンパイラーの拡張機能)で実際に書いているときにCで書いていると主張します。
  • そうしないエラーを非表示にすることができます:ノートの書き込みをするとき混乱のすべてのSO例type *対がtype **
  • #include適切なヘッダーファイルに失敗したことに気づかないようにするという考えは、木の森を見逃してしまいます。これは、「コンパイラーにプロトタイプが表示されないことについて文句を言うのに失敗したという事実について心配しないでください-その厄介なstdlib.hは、覚えておかなければならない本当に重要なことです!」
  • 追加の認知的クロスチェックを強制します。それは、その変数の生のサイズのためにあなたがしている算術のすぐ隣に、(主張された)望ましい型を置きます。きっとmalloc()、キャストがあるとバグがはるかに早く検出されることを示すSOの調査を行うことができるでしょう。アサーションと同様に、意図を明らかにする注釈はバグを減らします。
  • マシンがチェックできる方法で自分自身を繰り返すことは、多くの場合素晴らしいアイデアです。実際、それがアサーションとは何か、そしてこのキャストの使用はアサーションです。チューリングは何年も前にアイデアを思い付いたので、アサーションはコードを正しくするための最も一般的な手法です。

39
@ulidtkoご存じない場合は、CとC ++の両方としてコンパイルするコードを書くことができます。実際、ほとんどのヘッダーファイルはこのようなものであり、多くの場合、コード(マクロとインライン関数)が含まれています。持つ.c/ .cppの両方としてコンパイルするファイルは、非常に便利ではありませんが、一つのケースは、C ++の追加されたthrowC ++コンパイラでコンパイルするときのサポートを(ただし、return -1;Cコンパイラ、または任意でコンパイルした場合)。
ハイド

37
誰かがmalloc呼び出しをヘッダー内でインラインで実行した場合、私は感心しないでしょう、#ifdef __cplusplusとextern "C" {}はこのジョブ用であり、追加のキャストを追加していません。
ポール2013年

15
まあ、ポイント1は関係ありません。C!= C ++なので、呼び出しで変数を使用する場合、他のポイントも簡単です。その後、ループしてください。きちんとゼロに初期化された101文字の配列を割り当てます。キャストは必要ありません。宣言をその他のタイプに変更すれば、あなたはまだ上手ですmallocchar **foo = malloc(3*sizeof(*foo));foo[i] = calloc(101, sizeof(*(foo[i])));unsigned char
エリアスヴァンウーテジェム2013

34
私がそれを得たと思ったとき、それは来る!素晴らしい答え。ここStackOverflowで初めて、反対の2つの答えを+1するのは初めてです。+1いいえ、あなたはキャストしません、そして+1はい、あなたはキャストします!笑。あなたたちは素晴らしいです。そして私と私の生徒たちのために、私は心に決めました。生徒がキャストするときに発生する種類のエラーは、キャスト時に簡単に特定できます。
Beco博士、2014

15
@Leushenko:機械による検証も現地の検証もできない方法で自分自身を繰り返すことは悪いことです。そのような方法で検証できる方法で自分自身を繰り返すことはそれほど悪いことではありません。が与えられたstruct Zebra *p; ... p=malloc(sizeof struct Zebra);場合、mallocはpの型に関する情報の重複を回避できませんが、一方の型が変更されても他方の型が変更されない場合、コンパイラーもローカルコード検査も問題を検出しません。コードをに変更するp=(struct Zebra*)malloc(sizeof struct Zebra);と、キャストタイプが一致しない場合、コンパイラーがp
不規則に動作し

170

他の人が述べたように、Cには必要ありませんが、C ++には必要です。CコードをC ++コンパイラでコンパイルする予定がある場合は、何らかの理由で、代わりに次のようなマクロを使用できます。

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

そうすれば、非常にコンパクトな方法でそれを書くことができます:

int *sieve = NEW(int, 1);

CとC ++用にコンパイルされます。


17
とにかくマクロを使っnewているので、C ++の定義に使ってみませんか?
Hosam Aly

63
そうする理由がないからです。これは主に、C ++コンパイラーでコンパイルされたCプログラム用です。「新規」を使用する場合、唯一の問題は問題です。その後、無料のマクロも必要です。そして、あなたは、配列、Cには存在しない差別を解放するマクロを必要とする
quinmars

8
あなたがメモリを解放するのはあなたではなく、おそらくあなたが使用しているCライブラリなどであるかどうかは言うまでもありません。
quinmars 2009年

86
@Hosam:はい、そうです。使用newする場合は使用する必要があり、使用するdelete場合は使用malloc()する必要がありますfree()。それらを混ぜないでください。
Graeme Perrow、2011

17
このアプローチをとる場合NEW、リソースがdelete(またはDELETE)を使用して返されることはないので、マクロを呼び出すことはおそらくお勧めできません。代わりに、名前を付けるMALLOCCALLOC、この場合はもっとわかりやすくします。
ma 14

139

ウィキペディアから:

キャストの利点

  • キャストを含めると、Cプログラムまたは関数をC ++としてコンパイルできる場合があります。

  • キャストでは、元々char *を返していた1989年より前のバージョンのmallocを使用できます。

  • キャストは、特にポインターがmalloc()呼び出しから遠く離れて宣言されている場合、宛先ポインターのタイプが変更された場合に、開発者が型サイズの不整合を特定するのに役立ちます(ただし、最近のコンパイラーおよび静的アナライザーは、キャストを必要とせずにそのような動作について警告できます)。

キャストの短所

  • ANSI C標準では、キャストは冗長です。

  • キャストを追加すると、ヘッダーを含めることができなくなる可能性があります stdlib.hます、mallocのプロトタイプが見つかりました。mallocのプロトタイプがない場合、標準では、Cコンパイラがmallocがintを返すことを前提としています。キャストがない場合、この整数がポインターに割り当てられると警告が発行されます。ただし、キャストでは、この警告は生成されず、バグを隠します。特定のアーキテクチャとデータモデル(64ビットシステムのLP64など、longとポインタが64ビットで、intが32ビット)では、暗黙的に宣言されたmallocが32を返すため、このエラーは実際には未定義の動作を引き起こす可能性があります。実際に定義された関数は64ビット値を返しますが、ビット値。呼び出し規約とメモリレイアウトによっては、スタックスマッシュが発生する可能性があります。この問題は、最新のコンパイラでは見過ごされがちですが、宣言されていない関数が使用されたという警告を一律に生成するため、警告が表示されます。たとえば、GCCのデフォルトの動作では、キャストが存在するかどうかに関係なく、「組み込み関数の互換性のない暗黙の宣言」という警告が表示されます。

  • ポインターの型が宣言時に変更される場合、mallocが呼び出されてキャストされるすべての行を変更する必要がある場合もあります。

鋳造なしのmalloc関数は、好適な方法であると最も経験豊富なプログラマは、それを選択してください、あなたは問題を認識したような方、あなたを使用する必要があります。

つまり、CプログラムをC ++としてコンパイルする必要がある場合(それは別の言語ですが)、使用結果をキャストする必要がありますmalloc


1
キャストは、特にポインターがmalloc()呼び出しから遠くに宣言されている場合に、宛先ポインターのタイプが変更された場合に、開発者がタイプサイジングの不整合を特定するのに役立つ」とはどういう意味ですか?例を挙げていただけますか?
Spikatrix 2016年

3
@CoolGuy:以前のコメントで別の回答を参照してください。ただし、p = malloc(sizeof(*p) * count)イディオムはタイプの変更を自動的に取得するため、警告を受け取ったり、何かを変更したりする必要はありません。したがって、これは、キャスティングしないための最良の選択肢と比べて、実際の利点ではありません。
Peter Cordes

7
これは正しい答えです。長所と短所があり、好みの問題に要約されます(コードをC ++としてコンパイルする必要がある場合を除き、キャストは必須です)。
ピーター-2016年

3
ポインタの型がその宣言で変更された場合、mallocのすべてのインスタンスをチェックし、その型を再割り当てして解放する必要があるため、ポイント3は意味がない。キャスティングはあなたにそれを強制するでしょう。
–MichaëlRoy 2017

104

Cでは、voidポインターを暗黙的に他の種類のポインターに変換できるため、キャストは必要ありません。ある人を使うことは、それを必要とする理由がいくつかあることを偶然の観察者に示唆するかもしれません。


100

mallocの結果をキャストしないでください。キャストすると、コードに無意味な混乱が追加されます。

mallocの結果をキャストする最も一般的な理由は、C言語がどのように機能するかがわからないためです。これは警告のサインです。特定の言語メカニズムがどのように機能するかわからない場合は、推測を取ります。調べるか、Stack Overflowで質問してください。

いくつかのコメント:

  • voidポインターは、明示的なキャストなしで他のポインター型との間で変換できます(C11 6.3.2.3および6.5.16.1)。

  • ただし、C ++では、void*と別のポインタ型の間の暗黙的なキャストは許可されません。したがって、C ++では、キャストは正しかったでしょう。ただし、C ++でプログラミングする場合はnew、malloc()ではなく、使用する必要があります。また、C ++コンパイラを使用してCコードをコンパイルしないでください。

    同じソースコードでCとC ++の両方をサポートする必要がある場合は、コンパイラスイッチを使用して違いをマークします。互換性がないため、両方の言語標準を同じコードでまとめようとしないでください。

  • ヘッダーをインクルードするのを忘れたためにCコンパイラーが関数を見つけられない場合、そのことに関するコンパイラー/リンカーエラーが表示されます。したがって、それを含めるのを忘れた場合は<stdlib.h>、大きな問題ではありません。プログラムを構築することはできません。

  • 25年以上前の標準のバージョンに準拠している古いコンパイラでは、含める<stdlib.h>ことを忘れると危険な動作が発生します。その古い標準では、可視プロトタイプのない関数は暗黙的に戻り値の型をintです。mallocからの結果を明示的にキャストすると、このバグを隠すことができます。

    しかし、それは実際には問題ではありません。25年前のコンピューターを使用していないのに、なぜ25年前のコンパイラーを使用するのでしょうか。


9
「無意味な乱雑さ」は、あなたにまだ同意していない人を説得する可能性を狂わせる傾向がある、否定的な誇張です。キャストは無意味ではありません。Ron BurkとKazの答えは、私が非常に同意するキャストを支持することを主張しています。それらの懸念があなたが言及する懸念よりも重いかどうかは、尋ねるのに合理的な質問です。私にとって、あなたの懸念は彼らの懸念に比べて比較的軽微に見えます。
Don Hatch、

「voidポインターは、明示的なキャストなしで他のポインター型との間で変換できます」は、6.3.2.3ではサポートされていません。おそらく、「任意のオブジェクトタイプへのポインタ」を考えているのでしょうか。「ボイドポインター」と「関数へのポインター」はそれほど容易に変換できません。
chux-モニカを2017年

実際、参照は不完全でした。「暗黙性」に関連する部分は、単純な割り当てのルールです。「1つのオペランドはオブジェクト型へのポインタであり、もう1つはvoidの修飾バージョンまたは非修飾バージョンへのポインタです。」完全を期すために、この参照を回答に追加しました。
ランディン

91

Cではvoid *、他の(データ)ポインタからへの暗黙の変換を取得します。


6
@ジェンス:わかりました、おそらくより適切な表現は「暗黙の変換」です。浮動小数点式での整数変数の使用と同様。
EFraim 2012

@EFraimこれは実際にキャストを引き起こし、暗黙のキャストになります。
Mad Physicist

71

によって返された値をキャストする malloc()必要はありませんが、誰も指摘していないように見えるポイントを1つ追加したいと思います。

古代、つまりANSI Cvoid *がポインタの一般的なタイプとしてを提供する前は、そのchar *ような使用法のタイプです。その場合、キャストはコンパイラの警告をシャットダウンできます。

リファレンス:C FAQ


2
コンパイラの警告をシャットダウンすることは悪い考えです。
Albert van der Horst

8
@AlbertvanderHorst正確な問題を解決することによってそうしている場合は、警告が表示されます。
Dan Bechard、

@ダン。正確な問題を解決することによって、char *の代わりに最新のANSI C型を返すようにサブルーチンを書き換えることを意味する場合は、同意します。コンパイラをシャットダウンするとは言いません。可能性のある問題を見つけるために再コンパイルするたびに警告を使用するのではなく、コンパイラの警告がないと主張するマネージャーに譲らないでください。Groetjes Albert
Albert van der Horst

53

私の経験を追加するだけで、コンピューターエンジニアリングを勉強すると、Cで書いた2つか3つの教授が常にmallocをキャストすることがわかりますが、私が尋ねた(非常に高いCVとCを理解している)教授は、それは絶対に不要であると言いましたが、以前は絶対的に具体的であり、学生を絶対的に具体的であるという考え方に導くためにのみ使用されていました。基本的にキャストは、その動作方法には何も変更せず、それが言っていることを正確に実行し、メモリを割り当て、キャストはそれに影響せず、同じメモリを取得し、誤って他の何かにキャストしたとしても(コンパイラを回避してしまう)エラー)Cは同じ方法でアクセスします。

編集:キャスティングには一定のポイントがあります。配列表記を使用する場合、生成されたコードは、次の要素の先頭に到達するために進める必要のあるメモリ場所の数を知る必要があります。これは、キャストによって実現されます。これにより、doubleの場合は8バイト先に進み、intの場合は4バイト先に進むことがわかります。したがって、ポインタ表記を使用しても効果はなく、配列表記では必要になります。


3
既に述べた場合を除いて、キャストはバグを隠し、コンパイラーまたは静的アナライザー用にコードを分析しにくくする可能性があります。
ランディン2014年

2
「本質的にキャストしても、動作は変わりません」。一致する型にキャストしても何も変化しないはずですが、varの型が変化してキャストが一致しなくなった場合、問題が発生する可能性がありますか?IWO、キャストと変数タイプは同期を保つ必要があります-メンテナンス作業の2倍。
chux-モニカを復活させる2014年

教授がキャストを好む理由がわかります。キャスティングは、それがインストラクター情報に伝えられ、生徒のコードを維持する必要がない教育的な観点から役立つ場合があります-その使い捨てコード。しかし、コーディング、ピアレビュー、およびメンテナンスの観点からp = malloc(sizeof *p * n);は、とてもシンプルで優れています。
chux-モニカを2017年

53

はをmalloc返すため、の結果をキャストすることは必須ではなくvoid*、a void*は任意のデータ型を指すことができます。


34

voidポインターは汎用オブジェクトポインターであり、Cはvoidポインター型から他の型への暗黙的な変換をサポートしているため、明示的に型キャストする必要はありません。

ただし、暗黙的な変換をサポートしていないC ++プラットフォームで完全に互換性のある同じコードを機能させたい場合は、型キャストを行う必要があるため、すべてがユーザビリティに依存します。


2
単一のソースをCとC ++の両方としてコンパイルするのは通常の使用例ではありません(たとえば、宣言を含むヘッダーファイルを使用してCとC ++コードをリンクするのとは対照的です)。mallocC ++でのとの使用は、特別な注意(またはCでの書き換え)に値することを示す適切な警告サインです。
Toby Speight

1
「ボイドポインターはジェネリックポインターです」->「ボイドポインターはジェネリックオブジェクトポインターです」。関数ポインタのサイズはを超える可能 void *void *があるため、関数ポインタを適切に格納するには不十分です。
chux-モニカを復活させる'08年

その行の私の意図は同じでしたが、とにかく提案を@chuxに感謝します。
エンデバー

33

これは、GNU Cライブラリリファレンスマニュアルの内容です。

mallocISO Cはvoid *必要に応じて型を別の型のポインターに自動的に変換するため、キャストなしで任意のポインター変数に結果を格納できます。ただし、キャストは代入演算子以外のコンテキストで、またはコードを従来のCで実行したい場合に必要です。

実際、ISO C11標準(p347)は次のように述べています。

割り当てが成功した場合に返されるポインターは、基本的な配置要件を持つ任意のタイプのオブジェクトへのポインターに割り当てられるように適切に配置され、割り当てられたスペース内のそのようなオブジェクトまたはそのようなオブジェクトの配列にアクセスするために使用されます(スペースは明示的に割り当て解除されます)


31

返されるタイプはvoid *で、逆参照可能にするために、必要なタイプのデータポインターにキャストできます。


1
void* 目的のタイプにキャストできますが、自動的に変換されるため、その必要はありません。したがって、キャストは必要ありません。実際、高得点の回答で言及されている理由により望ましくありません。
Toby Speight

しかし、「オンザフライ」で逆参照する必要がある場合にのみ、代わりに変数を作成すると、キャストせずに(Cで)安全かつ自動的に変数の有効な型に変換されます。
Ferrarezi

28

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++) {
   ...
}

「ボイドポインターは任意のオブジェクトポインターに割り当てることができます」関数ポインターはmalloc()1つではありませんが、別の問題です。
chux-モニカの復活2017年

void*関数ポインターへの割り当て、または関数ポインターからの割り当ては情報を失う可能性があるため、「無効なポインターを任意のポインターに割り当てることができます」という問題が発生します。ただしvoid*、from malloc() を任意のオブジェクトポインタに割り当てることは問題ではありません。
chux-モニカの復活2017年

do離れて、表題の質問から疑問に思っている、まだループを含むマクロに関連するループコメント。そのコメントを削除します。これも後で削除します。
chux-モニカを2017年

27

それはプログラミング言語とコンパイラに依存します。mallocCで使用する場合は、自動的に型キャストされるため、型キャストする必要はありません。ただし、C ++を使用している場合はmallocvoid*型を返すため、キャストを入力する必要があります。


1
関数mallocはCでもvoidポインターを返しますが、言語の規則はC ++とは異なります。
8月Karlstrom

16

GCCとClangに慣れている人々は甘やかされて育った。それはそこに良いすべてではありません。

私は長年にわたって、私が使用することを要求された驚異的に古くなったコンパイラーにかなり恐怖を感じてきました。多くの場合、企業やマネージャーはコンパイラーの変更に非常に保守的なアプローチを採用しており、システムで新しいコンパイラー(標準への準拠とコードの最適化が優れている)が機能するかどうかをテストしません。作業している開発者にとって実際的な現実は、コーディングするときはベースをカバーする必要があることです。残念ながら、コードに適用するコンパイラーを制御できない場合は、mallocsのキャストが適切な習慣です。

また、多くの組織が独自のコーディング標準を適用し、それが定義されている場合、人々が従う方法であるべきだと提案します。明示的なガイダンスがない場合、私は標準への従順ではなく、どこでもコンパイルする可能性が最も高い傾向があります。

現在の標準では必要ないという主張は非常に有効です。しかし、その議論は現実世界の実用性を省略しています。私たちは、その日の標準によって独占的に支配された世界ではなく、私が「ローカルマネジメントの現実の分野」と呼んでいるものの実用性によってコーディングしません。そして、それは今までの時空よりも曲がりくねっています。:-)

YMMV。

私はmallocのキャストを防御的な操作と考えがちです。きれいではありませんが、完璧ではありませんが、一般的には安全です。(正直なところ、あなたはSTDLIB.H含まれていませんでしたならば、あなたがきたのmallocのキャストよりも多くの問題!)。


15

型変換の醜い穴の不承認を示すために単にキャストを挿入しました。これにより、次のスニペットなどのコードを診断なしでコンパイルできます。

double d;
void *p = &d;
int *q = p;

私はそれが存在しなかった(そしてそれがC ++には存在しなかった)ことを望み、キャストします。それは私の好み、そして私のプログラミング政治を表しています。私はポインターをキャストするだけでなく、効果的に、投票用紙をキャストし、愚かな悪魔をキャストします。私が実際に 愚かさを追い出すことができないなら、少なくとも抗議のしぐさでそうすることの願いを表現させてください。

実際、mallocを返す関数でラップ(およびフレンド)しunsigned char *、基本的にvoid *コードで使用しないことをお勧めします。任意のオブジェクトへの汎用ポインタが必要な場合は、char *またはを使用unsigned char *し、双方向にキャストします。おそらくリラックスできるリラクゼーションの1つは、キャストのような関数memsetmemcpyキャストのない関数を使用することです。

キャストとC ++の互換性のトピックで、CとC ++の両方としてコンパイルされるようにコードを記述した場合(この場合、以外に割り当てるときにの戻り値キャストする必要があります)、非常に役立ちます。 C ++としてコンパイルする場合はC ++スタイルのキャストに変換するキャスト用のマクロを使用できますが、Cとしてコンパイルする場合はCキャストに縮小できます。mallocvoid *

/* 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_qualconstまたはを削除するためだけに使用し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コードとC ++コードを混在させないことをお勧めします。ただし、それを行う正当な理由がある場合は、マクロが役立つ場合があります。
Phil1970 2016年

@ Phil1970それはすべてCとC ++コンパイラに移植可能で、C ++のいくつかの機能を利用する1つのまとまりのある方言ですべて記述されています。これは、すべてのC ++としてコンパイル、または他のすべてのC.としてコンパイルする必要があります
カズ

つまり、前のコメントで私が言おうとしていたのは、CとC ++が混在していないということです。その意図は、コードがすべてCとしてコンパイルされるか、すべてC ++としてコンパイルされることです。
Kaz

15

Cでプログラミングするときは、可能な限り行うことをお勧めします。

  1. すべての警告をオンにしてCコンパイラでプログラムをコンパイルし、-Wallすべてのエラーと警告を修正します。
  2. として宣言されている変数がないことを確認してください auto
  3. その後でC ++コンパイラを使ってコンパイル-Wallして-std=c++11。すべてのエラーと警告を修正します。
  4. ここで再びCコンパイラを使用してコンパイルします。これで、プログラムは警告なしにコンパイルされ、含まれるバグは少なくなります。

この手順では、C ++の厳密な型チェックを利用できるため、バグの数を減らすことができます。特に、この手順では、強制的に含めるstdlib.hか、または取得します

malloc このスコープ内で宣言されていません

また、結果のキャストを強制します。そうしmallocないと、

からvoid*への無効な変換T*

またはあなたのターゲットタイプは何ですか。

私が見つけることができるC ++ではなくCで書くことの唯一の利点は

  1. Cは明確に指定されたABIを持っています
  2. C ++はより多くのコードを生成する可能性があります[例外、RTTI、テンプレート、実行時ポリモーフィズム]

Cに共通のサブセットを静的なポリモーフィック機能と併用すると、2番目の短所が理想的なケースではなくなることに注意してください。

C ++の厳格なルールが不便であると感じる人のために、推論された型でC ++ 11機能を使用できます

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...

18
CコードにはCコンパイラを使用します。C ++コードにはC ++コンパイラを使用します。イフもノーもない。CコードをC ++で書き直すことはまったく別のことであり、時間とリスクに見合う価値がある場合とそうでない場合があります。
Toby Speight

2
@TobySpeightのアドバイスに追加したいのですが、C ++プロジェクトでCコードを使用する必要がある場合は、通常、CコードをC(eg gcc -c c_code.c)としてコンパイルし、C ++コードをC ++(eg g++ -c cpp_code.cpp)としてコンパイルして、それらをリンクします。(たとえばgcc c_code.o cpp_code.o、プロジェクトの依存関係に応じて、またはその逆)。いずれかの言語の優れた機能を奪う理由は何もないはずです...
自閉症の人2015

1
@ user877329これは、「C ++互換」であるという理由だけで、コードの可読性を低下させるコードにキャストを入念に追加するよりも賢明な代替手段です。
自閉症の2015

1
おそらく、このコンテキストの主な利点は、Cでを作成できることですp = malloc(sizeof(*p));。これpにより、別の型名に変更した場合、最初に変更する必要はありません。提案されているキャストの「利点」pは、型が間違っているとコンパイルエラーが発生することですが、正しく機能する場合はさらに優れています。
Peter Cordes

1
適切なC ++コンパイラが不足しているプラ​​ットフォームをターゲットにする場合、Cでの記述が必要になる場合があることを述べておきます。例外とテンプレートは、典型的には、ヘルプC ++ C ++におけるランタイム多型がCにほとんど同等であるが小さく、かつ/またはより効率的なコードを生成する機能である
user7860670

15

いいえ、結果をキャストしませんmalloc()

一般に、から、またはへのキャストは行いませんvoid *

そうしないために与えられる典型的な理由は、失敗#include <stdlib.h>が見過ごされる可能性があることです。C99が暗黙の関数宣言を不正にしたため、これは長い間問題ではなくなりました。コンパイラが少なくともC99に準拠している場合、診断メッセージが表示されます。

しかし、不必要なポインタキャストを導入しないほうはるかに強力な理由があります。

Cでは、ポインタキャストはほとんど常にエラーです。これは、次のルールによるものです(C11の最新のドラフトであるN1570の6.5 p7)。

オブジェクトは、次のタイプのいずれかを持つ左辺値式によってのみアクセスされる格納された値を持つ必要があります。—
オブジェクト
の有効なタイプと互換性のあるタイプ
— — オブジェクトの有効なタイプと互換性のあるタイプの修飾バージョン、—オブジェクトの有効な型に対応する符号付きまたは符号なしの型で
ある型— オブジェクトの有効な型の修飾バージョンに対応する符号付きまたは符号なしの型である型
— 1つを含む集合体または共用体型メンバー間の前述のタイプ(再帰的には、サブアグリゲートまたは含まれるユニオンのメンバーを含む)、または
—文字タイプ。

これは、厳密なエイリアスルールとも呼ばれます。したがって、次のコードは未定義の動作です。

long x = 5;
double *p = (double *)&x;
double y = *p;

そして、時には意外なことに、以下も同様です。

struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;

場合によっては、ポインタをキャストする必要があります厳密なエイリアシングルールが与えられているため、非常に注意する必要があります。したがって、コード内でキャストされたポインタは、その有効性を再確認する必要がある場所です。したがって、不要なポインターキャストを記述することはありません。

tl; dr

一言で言えば:Cには、のでどんなの発生ポインタキャストは、特別な注意が必要なコードのための赤い旗を上げる必要があり、あなたは書くべきではありません不要なポインタのキャストを。


サイドノート:

  • 実際にへのキャストが必要な場合がありますvoid *。たとえば、ポインタを出力したい場合は、次のようになります。

    int x = 5;
    printf("%p\n", (void *)&x);

    キャストはprintf()可変個の関数であるため、ここで必要です。したがって、暗黙的な変換は機能しません。

  • C ++では状況が異なります。ポインタ型のキャストは、派生クラスのオブジェクトを処理するときにいくぶん一般的(かつ正しい)です。したがって、C ++では、暗黙的な変換void *行われません。C ++には、さまざまな種類のキャストがあります。


1
あなたの例ではvoid *を避けています。double *からint *へのキャストとその逆のキャストには違いがあります。mallocは最大の標準型に整列されたpointelを返すため、誰かがこの整列されたポインターを他の型にキャストしたとしても、エイリアシングルールは破られません。
P__J__ 2017

エイリアスは、アラインメントやコメントの残りの部分とはまったく関係がありません -明らかにポイントがありませんでした。

@PeterJ:念のため、不必要なポインタキャストを回避することがポイントなので、特別な注意を払う必要のあるコードのように見えません。

厳密なエイリアシングの問題は、実際にはvoidポインターとは何の関係もありません。厳密なエイリアス違反によって引き起こされるバグを取得するには、ポイントされたデータの参照を解除する必要があります。また、voidポインターを逆参照することはできないため、そのようなバグは定義ごとにvoidポインターではなく他のものに関連しています。
ランディン2017

むしろ、すべてのポインターキャストを禁止するルールを作成する必要があります。しかし、シリアライゼーションルーチンやハードウェア関連のプログラミングなどをどのように記述しますか?Cの強みであるもの。このようなキャストは、自分が何をしているのかわかっている場合は問題ありません。
ランディン2017

15

私はキャストを好むが、手動ではない。私のお気に入りは、使用しているg_newg_new0のglibからマクロ。glibを使用しない場合は、同様のマクロを追加します。これらのマクロは、型の安全性を損なうことなくコードの重複を減らします。型が間違っていると、非voidポインター間の暗黙的なキャストが発生し、警告(C ++のエラー)が発生します。g_newand を定義するヘッダーを含めるのを忘れるとg_new0、エラーが発生します。g_newそして、g_new0両方が同じ引数mallocをとりますcalloc。追加0するだけで、ゼロで初期化されたメモリを取得できます。コードは、C ++コンパイラで変更せずにコンパイルできます。


12

キャストはC ++専用であり、Cではありません。C++コンパイラを使用している場合は、Cコンパイラに変更することをお勧めします。


9

voidポインターの背後にある概念は、任意のデータ型にキャストできることです。これが、mallocがvoidを返す理由です。また、自動型キャストにも注意する必要があります。したがって、ポインタをキャストすることは必須ではありませんが、実行する必要があります。コードをクリーンに保ち、デバッグに役立ちます


11
それは必須ではありません-あなたがそれをしなければなりませんが」-私はそこに矛盾があると思います!
Toby Speight

5
私はあなたが誰かにこの投稿を読んで、彼らがあなたが言おうとしていることを理解しているかどうか見るべきだと思います。次に、それを書き直して、言いたいことを明確にします。私はあなたの答えが何であるか本当に理解できません。
ビル・ウッドガー、2015

9

voidポインターは汎用ポインターであり、Cはvoidポインター型から他の型への暗黙的な変換をサポートしているため、明示的に型キャストする必要はありません。

ただし、暗黙的な変換をサポートしていないC ++プラットフォームで完全に互換性のある同じコードを機能させたい場合は、型キャストを行う必要があるため、すべてがユーザビリティに依存します。


9
  1. 他の述べたように、それはCではなくC ++に必要です。

  2. キャストを含めると、Cプログラムまたは関数をC ++としてコンパイルできる場合があります。

  3. Cではvoid *が自動的かつ安全に他のポインタ型に昇格されるため、これは不要です。

  4. しかし、キャストした場合、stdlib.hをインクルードするのを忘れた場合、エラーを隠すことができ ます。これにより、クラッシュが発生する可能性があります(さらに悪いことに、コードの完全に異なる一部の部分まではクラッシュしません)。

    stdlib.hにはmallocのプロトタイプが含まれているため、これが見つかります。mallocのプロトタイプがない場合、標準では、Cコンパイラがmallocがintを返すことを前提としています。キャストがない場合、この整数がポインターに割り当てられると警告が発行されます。ただし、キャストでは、この警告は生成されず、バグを隠します。


7

Cではmallocのキャストは不要ですが、C ++では必須です。

次の理由により、Cではキャストは不要です。

  • void * Cの場合、他のポインタ型に自動的かつ安全に昇格されます。
  • 含めるのを忘れた場合、エラーを隠すことができます<stdlib.h>。これにより、クラッシュが発生する可能性があります。
  • ポインターと整数のサイズが異なる場合は、キャストによって警告を非表示にしているため、返されたアドレスの一部が失われる可能性があります。
  • ポインタの型が宣言時に変更された場合、malloc呼び出されてキャストされるすべての行を変更する必要がある場合もあります。

一方、キャストを行うとプログラムの移植性が向上する場合があります。つまり、Cプログラムまたは関数をC ++としてコンパイルできます。


0

私にとっては、ここでテイクホームおよび結論は、鋳造ということであるmallocCには全く必要ありませんが、あなたはしかしキャスト場合、それが影響を与える文句を言わないmallocmalloc、まだあなたにあなたの要求恵まれメモリ空間を割り当てます。別の持ち帰りは、理由またはキャストを行う理由の1つです。これは、CまたはC ++で同じプログラムをコンパイルできるようにするためです。

他の理由があるかもしれませんが、他の理由は、ほぼ間違いなく、遅かれ早かれあなたを深刻な問題に陥らせるでしょう。


0

可能ですが、Cでキャストする必要はありません。そのコードがC ++としてコンパイルされている場合はキャストする必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.