質問

この質問では、誰かがコメントで提案されている は、 malloc の結果をキャストしない、つまり

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

ではなく:

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

なぜそうなるのでしょうか

役に立ちましたか?

解決

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

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

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

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

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

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

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


length を前面に移動すると まれに可視性が向上する場合がありますが、一般的な場合は式を記述する方が良いことに注意する必要がありますas:

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

sizeof を最初に保持するため、この場合、少なくとも size_t 数学で乗算が行われるようにします。

比較: malloc(sizeof * sieve * length * width) vs. malloc(length * width * sizeof * sieve) 2番目は length * width および length size_t よりも小さい型の場合、width

他のヒント

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

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

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

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

あなたがやるキャストは、次の理由によります:

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

他にも述べたように、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 ++用にコンパイルします。

ウィキペディアから:

  

キャストの利点

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

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

  •   
  • キャスティングは、特にポインターがmalloc()呼び出しから遠く離れて宣言されている場合、デベロッパーが型のサイズ変更の不一致を特定するのに役立ちます(ただし、最新のコンパイラーおよび静的アナライザーは、キャスト)。

  •   
     

キャストの短所

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

  •   
  • キャストを追加すると、ヘッダー stdlib.h のインクルードの失敗がマスクされる場合があります。   mallocのプロトタイプが見つかりました。がない場合   mallocのプロトタイプ。標準ではCコンパイラが必要です。   mallocがintを返すと仮定します。キャストがない場合、警告は   この整数がポインターに割り当てられたときに発行されます。ただし、   キャスト、この警告は生成されず、バグを隠します。特定の   アーキテクチャとデータモデル(64ビットシステム上のLP64など、   longおよびポインターは64ビットで、intは32ビットです)、このエラーは   暗黙的に宣言されているように、実際には未定義の動作になります   実際に定義された関数に対して、mallocは32ビット値を返します   64ビット値を返します。呼び出し規約とメモリに依存   レイアウトでは、これによりスタックが破壊される場合があります。この問題はあまり起こりません   均一に生成されるため、現代のコンパイラでは気付かれずに   未宣言の関数が使用されたことを警告するため、警告が   まだ表示されます。たとえば、GCCのデフォルトの動作は、   組み込みの"互換性のない暗黙の宣言を読み取る警告   関数"キャストが存在するかどうかに関係なく。

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

  •   

キャストなしのmallocが推奨される方法であり、ほとんどの経験豊富なプログラマーがそれを選択しますが、問題を認識したい方を使用する必要があります。

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

Cでは、暗黙的にvoidポインターを他の種類のポインターに変換できるため、キャストは必要ありません。 1つを使用すると、必要な理由があることを偶然の観察者に示唆する可能性があり、誤解を招く可能性があります。

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

人々がmallocの結果をキャストする最も一般的な理由は、C言語がどのように動作するのかわからないためです。これは警告サインです。特定の言語メカニズムがどのように機能するかわからない場合は、推測しないでください 。調べるか、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年前のコンパイラーを使用するのですか?

Cでは、 void * から他の(データ)ポインターに暗黙的に変換されます。

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

古代では、つまり、 ANSI C が汎用タイプのポインターとして void * を提供する前、 char * はそのような使用法のタイプ。その場合、キャストによりコンパイラの警告がシャットダウンされる可能性があります。

リファレンス: C FAQ

void * を返すため、 malloc の結果をキャストすることは必須ではなく、 void * はデータ・タイプ。

私の経験を加えて、コンピューター工学を勉強していると、Cで書いている2人または3人の教授が常にmallocをキャストしていることがわかりますが、私が尋ねた人は(Cの膨大なCVと理解で)絶対に不必要だが、かつては絶対に具体的であり、学生を絶対に具体的であるという精神に導いていた。基本的に、キャストは動作方法に何も変更せず、正確に言うことを行い、メモリを割り当て、キャストはそれに影響を与えず、同じメモリを取得し、誤って別のものにキャストしても(そして何らかの形でコンパイラを回避します) errors)Cは同じ方法でアクセスします。

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

これが GNU Cライブラリリファレンスマニュアルによると:

  

malloc の結果は、   キャスト。ISOCは自動的に void * 型を別の型に変換するため   必要な場合のポインターのタイプ。しかし、キャストはコンテキストで必要です   代入演算子以外、またはコードを実行したい場合   従来のCで。

そして確かに ISO C11標準(p347)そう言う:

  

割り当てが成功した場合に返されるポインターは適切に位置合わせされます   任意のタイプのオブジェクトへのポインタに割り当てられる可能性があること   基本的なアライメント要件、およびそのようなアクセスに使用される   割り当てられたスペース内のオブジェクトまたはそのようなオブジェクトの配列(   スペースは明示的に割り当て解除されます)

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

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

返される型は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++) {
   ...
}

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

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

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

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

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

YMMV。

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

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

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

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

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

キャストとC ++の互換性のトピックで、CとC ++の両方としてコンパイルするようにコードを記述した場合(この場合、 malloc <の戻り値を キャストする必要があります) / code>を 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 ++で定期的にコードをコンパイルすると、適切なキャストの使用が強制されます。たとえば、 const または volatile を削除するためだけに strip_qual を使用するが、型変換が行われるようにプログラムが変更された場合関与している場合は、診断を取得し、キャストの組み合わせを使用して目的の変換を取得する必要があります。

これらのマクロを順守するために、GNU C ++(C!ではありません)コンパイラには美しい機能があります。Cスタイルキャストのすべての発生に対して生成されるオプションの診断機能です。

     -Wold-style-cast (C++ and Objective-C++ only)
         Warn if an old-style (C-style) cast to a non-void type is used
         within a C++ program.  The new-style casts (dynamic_cast,
         static_cast, reinterpret_cast, and const_cast) are less vulnerable
         to unintended effects and much easier to search for.

CコードがC ++としてコンパイルされる場合、この -Wold-style-cast オプションを使用して、(type)キャスト構文のすべての出現を見つけることができます。コードに忍び込み、上記のマクロ(または必要に応じて組み合わせ)の中から適切な選択肢に置き換えて、これらの診断をフォローアップします。

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

可能な限りCでプログラミングする場合の最善策:

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

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

  

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

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

  

void * から T *

への無効な変換

またはターゲットタイプは何でも。

見つけることができるC ++の代わりにCで書くことの唯一の利点は次のとおりです

  1. Cには適切に指定されたABIがあります
  2. C ++はより多くのコードを生成する可能性があります[例外、RTTI、テンプレート、ランタイムポリモーフィズム]

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

C ++の厳格なルールが不便な場合は、C ++ 11機能を推論型で使用できます

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

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

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

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

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

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

  

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

これは、 strict aliasing rule とも呼ばれます。したがって、次のコードは未定義の動作です:

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 ++には、さまざまな種類のキャストがあります。

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

キャスティングはC ++ではなくC ++専用です。C++コンパイラを使用している場合は、Cコンパイラに変更することをお勧めします。

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

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

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

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

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

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

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

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

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

Cでは、次の理由でキャスティングが不要です。

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

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top