web-dev-qa-db-ja.com

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

この質問 で、誰かが コメント で提案しました、notmallocの結果をキャストするべきです。

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

のではなく:

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

これはなぜでしょうか。

2236

いいえ;あなたは結果をキャストしない

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

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

また、コメンテーターが指摘するように、上記は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)は、widthおよびlengthlength * widthよりも小さい型である場合、size_tをオーバーフローする可能性があります。

2105
unwind

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

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

これにより、sieveの型を変更したとしても、式の右側を変更することを心配する必要がなくなります。

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

351
dirkgently

あなたはdo cast、なぜなら:

  • あなたのコードを作成しますより移植性の高い CとC++の間で、SOの経験が示すように、非常に多くのプログラマーはC++(またはCさらに、ローカルコンパイラ拡張機能)。
  • そうしなかった場合エラーを隠すことができますtype *type **を書くタイミングを混乱させるSOの例に注意してください。
  • #includeに失敗したことに気付かないようにするという考えは、適切なヘッダーファイルが欠落している木の森を逃しています。 「プロトタイプが表示されないことについて不平を言うようにコンパイラに依頼しなかったという事実を心配しないでください。厄介なstdlib.hは本当に覚えておくべき重要なものです!」
  • 余分な認知的クロスチェックを強制します。この変数の生のサイズに対して実行している算術演算のすぐ隣に、(疑わしい)必要な型を配置します。 SOの調査を行って、キャストがあるとmalloc()バグがはるかに早く検出されることを確認できます。アサーションと同様に、意図を明らかにする注釈はバグを減らします。
  • マシンがチェックできる方法で自分自身を繰り返すことは、多くの場合greatアイデアです。実際、それがアサーションであり、キャストのこの使用はアサーションです。チューリングは何年も前にアイデアを思いついたので、アサーションは依然としてコードを正しく取得するための最も一般的な手法です。
329
Ron Burk

他の人が述べたように、それは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++用にコンパイルされます。

159
quinmars

Wikipediaから

キャスティングの利点

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

  • キャストは、元々char *を返していた1989年以前のバージョンのmallocを許可します。

  • 特にポインタがmalloc()呼び出しから遠く離れて宣言されている場合、キャストは型指定の矛盾を開発者が識別するのに役立ちます(ただし、最近のコンパイラや静的アナライザはキャストを必要とせずにそのような動作を警告できます).

キャスティングのデメリット

  • ANSI C規格では、キャストは冗長です。

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

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

キャストなしの mallocは推奨される方法であり、ほとんどの経験豊富なプログラマは を選択しますが、問題に気付いた方はどちらを使用してもかまいません。

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

118
ashiquzzaman33

Cでは、暗黙的にvoidポインターを他の種類のポインターに変換できるので、キャストは必要ありません。 1を使用することは、それが必要とされる理由があることをカジュアルな観察者に示唆するかもしれず、それは誤解を招く可能性があります。

99
PaulJWilliams

Mallocの結果はキャストしないでください。これを行うと、コードに無意味な混乱が生じます。

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

いくつかのコメント:

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

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

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

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

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

    しかし、それは本当に問題ではありません。あなたは25歳のコンピュータを使っていないのですが、25歳のコンパイラを使うのはなぜでしょうか。

89
Lundin

Cでは、void*から他の(データ)ポインタへの暗黙的な変換が行われます。

86
EFraim

malloc()によって返された値をキャストすることは今必要ではありません、しかし私は誰も指摘していないように思われる1つの点を加えたいと思います:

昔は、つまり、 ANSI C がポインタのジェネリック型としてvoid *を提供する前は、char *がそのような使い方のための型です。その場合、キャストはコンパイラの警告を停止させる可能性があります。

参照先: C FAQ

66
Yu Hao

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

49
user968000

私の経験を加えて、コンピュータ工学を勉強しただけで、私がCで書くのを見た2人か3人の教授は常にmallocを投げかけると思います。かつては絶対的に特定的であり、学生を絶対的に特定的であるという考え方にしていました。基本的にキャストしても動作は変わらず、指示どおりに動作し、メモリを割り当てても、キャストしても効果はありません。同じメモリが使用されます。 errors)Cは同じ方法でそれにアクセスします。

編集: /キャスティングには特定のポイントがあります。配列表記を使用するとき、生成されたコードは次の要素の先頭に到達するためにどれだけのメモリ位置を進める必要があるかを知っている必要があります。このようにして、doubleでは8バイト、intでは4バイトとなることがわかります。したがって、ポインタ表記を使用しても効果はありません。配列表記では必要になります。

48
user3079666

これが GNU Cライブラリリファレンス manualが言うことです。

ISO Cでは必要に応じてvoid *型が別の型のポインタに自動的に変換されるため、mallocの結果をキャストなしで任意のポインタ変数に格納できます。しかし、キャストは代入演算子以外の文脈で、あるいはあなたのコードを伝統的なC言語で実行したい場合に必要です。

そして、 ISO C11標準 (p347)はこう言っています。

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

30
Slothworks

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

しかし、暗黙の型変換をサポートしていないC++プラットフォームで同じコードを完全に互換性があるように動作させるには、型キャストを実行する必要があるため、使いやすさに依存します。

29
Endeavour

返される型はvoid *です。これは、参照解除できるように、希望する型のデータポインタにキャストできます。

28
swaps

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

プログラミング言語とコンパイラによって異なります。 Cでmallocを使用する場合は、自動的に型キャストされるので型キャストする必要はありません。ただし、C++を使用する場合はmallocvoid*を返すため、castと入力してください。タイプ。

25
Jeyamaran

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

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

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

現在の標準では必要ないという議論は非常に有効です。しかし、その議論は現実世界の実用性を省略しています。私たちはその日の基準だけで支配される世界ではなく、私が「ローカル経営の現実フィールド」と呼ぶのが好きなものの実用性によってコード化されています。そしてそれは、時空がかつてないほど曲がり、ねじれています。 :-)

YMMV。

私はmallocをキャストすることを防御的な操作と考える傾向があります。きれいでも、完璧でもありませんが、一般に安全です。 (正直なところ、stdlib.hをインクルードしていない場合、mallocをキャストするよりもwayより多くの問題があります!)。

16
StephenG

型システムのい穴の不承認を示すためにキャストを入れました。これにより、悪い変換を引き起こすためにキャストが使用されていなくても、次のスニペットなどのコードを診断なしでコンパイルできます。

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

私はそれが存在しなかったこと(そしてC++にはないこと)を望み、キャストしました。それは私の趣味とプログラミングの政治を表しています。私はポインターを投げるだけでなく、事実上、投票を投げ、 愚かな悪魔を追い出します 。私が実際に愚かさ をキャストできない場合、少なくとも私がやりたいことを表現させてください抗議のジェスチャーで。

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

キャストとC++の互換性のトピックで、CとC++の両方としてコンパイルするようにコードを作成する場合(この場合、をキャストします) mallocの戻り値をvoid *以外に割り当てると、非常に役立つことができます。C++としてコンパイルするときに、C++スタイルのキャストに変換するキャスト用のマクロを使用できますが、 CとしてコンパイルするときにCキャストに縮小します。

/* 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または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)キャスト構文のすべての出現箇所を見つけ、それを上記のマクロの中から適切な選択(または必要に応じて組み合わせ)。

この変換の処理は、「Clean C」で動作するための単一の最大のスタンドアロン技術的正当化です。つまり、CとC++の組み合わせ方言は、mallocの戻り値のキャストを技術的に正当化します。

14
Kaz

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

一般的に、あなたはvoid *との間でキャストしないでください

そうしない典型的な理由は、#include <stdlib.h>の失敗が気付かない可能性があることです。 C99が暗黙的な関数宣言を違法にしたため、これはもはや長い間問題ではないため、コンパイラが少なくともC99に準拠している場合、診断メッセージを取得します。

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

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

オブジェクトは、次のタイプのいずれかを持つ左辺値式によってのみアクセスされる保存された値を持たなければなりません:
—オブジェクトの有効なタイプと互換性のあるタイプ、
—オブジェクトの有効なタイプと互換性のあるタイプの認定バージョン、
—オブジェクトの有効なタイプに対応する符号付きまたは符号なしのタイプであるタイプ、
—オブジェクトの有効なタイプの修飾バージョンに対応する署名付きまたは署名なしのタイプであるタイプ、
—そのメンバー(前述のサブアグリゲートまたは包含ユニオンのメンバーを再帰的に含む)の中に前述のタイプの1つを含む集約またはユニオンタイプ、または
—文字タイプ。

これは、strict aliasing ruleとも呼ばれます。したがって、次のコードはundefined behaviorです。

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;

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

tl; dr

一言で言えば:Cではanypointer castの出現は特別な注意を必要とするコードに対して赤いフラグを立てる必要があるため、 不必要なポインタキャストを決して書かないでください。


サイドノート:

  • void *へのキャストを実際に必要とする場合があります。ポインターを印刷する場合:

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

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

  • C++では、状況は異なります。派生クラスのオブジェクトを扱う場合、ポインター型のキャストはやや一般的(かつ正しい)です。したがって、C++では、void *との間の変換はnot暗黙的です。 C++には、さまざまな種類のキャストがあります。

13
user2371524

可能なときはいつでもCでプログラミングするときにすべき最善のこと:

  1. -Wallをオンにしてすべての警告をオンにしてCコンパイラを介してプログラムをコンパイルし、すべてのエラーと警告を修正します。
  2. autoとして宣言された変数がないことを確認してください
  3. それから-Wall-std=c++11を付けてC++コンパイラを使ってコンパイルします。すべてのエラーと警告を修正してください。
  4. もう一度Cコンパイラを使用してコンパイルします。あなたのプログラムは警告なしでコンパイルされ、バグも少なくなるはずです。

この手順では、C++の厳密な型チェックを利用できるため、バグの数を減らすことができます。特に、この手続きはあなたにstdlib.hをインクルードすることを強制します。

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

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

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

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

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

  1. Cは明確に指定されたABIを持っています
  2. C++はもっと多くのコードを生成するかもしれません[例外、RTTI、テンプレート、 ランタイム ポリモーフィズム]

理想的な場合、Cに共通のサブセットを static polymorphic機能と一緒に使用すると、2番目の欠点が消えるはずです。

C++の厳密な規則が不便であると感じる人には、推論型でC++ 11の機能を使うことができます。

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

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

11
proski

キャスティングはC++専用ではありません。C++コンパイラーを使用している場合は、Cコンパイラーに変更してください。

10
user3949394

Voidポインタの背後にある概念は、mallocがvoidを返す理由である任意のデータ型にキャストできるということです。また、自動型キャストに注意する必要があります。だからあなたはそれをしなければならないけれどもそれはポインタをキャストすることは必須ではありません。コードをきれいに保つのに役立ち、デバッグに役立ちます。

9
iec2011007

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

しかし、暗黙の型変換をサポートしていないC++プラットフォームで同じコードを完全に互換性があるように動作させるには、型キャストを実行する必要があるため、使いやすさに依存します。

6
  1. 他の人が述べたように、それはCではなくC++のために必要である。

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

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

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

    stdlib.h にはmallocのプロトタイプが含まれているためです。 mallocのプロトタイプが存在しない場合、規格では、mallocがintを返すとCコンパイラが想定することが要求されています。キャストがない場合、この整数がポインタに割り当てられたときに警告が出されます。ただし、キャストでは、この警告は生成されず、バグが隠されています。

6
Mohit

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

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

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

一方で、キャストはプログラムの移植性を高める可能性があります。つまり、Cプログラムまたは関数をC++としてコンパイルすることができます。

3
Aashish