21

あるタイプへのポインターが別のタイプのポインターに変換される可能性があることを私は知っています。私は3つの質問があります:

  1. ポインターを型キャストする際に留意すべきことは何ですか?
  2. 結果のポインタで発生する可能性のある例外/エラーは何ですか?
  3. 例外/エラーを回避するためのベストプラクティスは何ですか?
4

3 に答える 3

16

よく書かれたプログラムは通常、ポインタ型キャストをあまり使用しません。たとえば、 mallocにptr型キャストを使用する必要があるかもしれません(宣言されています(void *)malloc(...))が、Cでは必要ありません(一部のコンパイラは文句を言うかもしれませんが)。

  int *p = malloc(sizeof(int)); // no need of (int *)malloc(...)

ただし、システムアプリケーションでは、トリックを使用してバイナリまたは特定の操作を実行したい場合があります。そのためには、マシン構造に近い言語であるCが便利です。たとえば、doubleのバイナリ構造(IEEE 754実装に続く)を分析したいとし、バイナリ要素の操作がより簡単であるとすると、次のように宣言できます。

  typedef unsigned char byte;
  double d = 0.9;
  byte *p = (byte *)&d;
  int i;
  for (i=0 ; i<sizeof(double) ; i++) { ... work with b ... }

ユニオンを使用することもできます。これは例です。

より複雑な使用法は、C ++ポリモーフィズムのシミュレーションである可能性があります。これは、「クラス」(構造)階層をどこかに格納して、何が何であるかを記憶し、ポインター型キャストを実行して、たとえば、親の「クラス」ポインター変数をある時点で派生クラスを指す(C ++リンクも参照)

  CRectangle rect;
  CPolygon *p = (CPolygon *)&rect;
  p->whatami = POLY_RECTANGLE; // a way to simulate polymorphism ...
  process_poly ( p );

ただし、この場合は、C++を直接使用する方がよいかもしれません。

ポインタ型キャストは、開発を開始する前に、プログラム分析の一部である明確に決定された状況に注意深く使用する必要があります。

ポインター型キャストの潜在的な危険性

  • 必要のないときにそれらを使用してください-それはエラーが発生しやすく、プログラムを複雑にします
  • アクセスオーバーフロー、間違った結果につながる可能性のある異なるサイズのオブジェクトを指しています...
  • 次のような2つの異なる構造へのポインタs1 *p = (s1 *)&s2;:サイズと配置に依存すると、エラーが発生する可能性があります

(しかし、公平を期すために、熟練したCプログラマーは上記の間違いを犯しません...)

ベストプラクティス

  • 必要な場合にのみ使用し、必要な理由を説明する部分にコメントを付けてください
  • あなたが何をしているのかを知ってください-熟練したプログラマーは間違いなく大量のポインタータイプキャストを使用するかもしれません、つまり試してみないでください、それはそのようなシステム/バージョン/ OSで動作するかもしれませんし、別のものでは動作しないかもしれません
于 2013-01-17T08:43:09.707 に答える
3

プレーンCでは、任意のポインター型を他の任意のポインター型にキャストできます。互換性のないタイプへのポインタまたは互換性のないタイプからのポインタをキャストし、メモリを誤って書き込むと、アプリケーションからセグメンテーション違反または予期しない結果が発生する可能性があります。

構造体ポインタをキャストするサンプルコードは次のとおりです。

struct Entity { 
  int type;
}

struct DetailedEntity1 {
  int type;
  short val1;
}

struct DetailedEntity2 {
  int type;
  long val;
  long val2;
}

// random code:
struct Entity* ent = (struct Entity*)ptr;

//bad:
struct DetailedEntity1* ent1 = (struct DetailedEntity1*)ent;
int a = ent->val; // may be an error here, invalid read
ent->val = 117; // possible invali write

//OK:
if (ent->type == DETAILED_ENTITY_1) {
  ((struct DetailedEntity1*)ent)->val1;
} else if (ent->type == DETAILED_ENTITY_2) {
  ((struct DetailedEntity2*)ent)->val2;
} 

関数ポインタに関しては、宣言に正確に適合する関数を常に使用する必要があります。そうしないと、予期しない結果やsegfaultが発生する可能性があります。

ポインタからポインタ(構造体かどうか)にキャストするときは、メモリがまったく同じ方法で整列されていることを確認する必要があります。構造全体をキャストする場合、最初に同じ変数の同じ順序を使用し、「共通ヘッダー」の後でのみ構造を区別することが最善の方法です。また、メモリの配置はマシンごとに異なる可能性があるため、構造体ポインタをバイト配列として送信して受信することはできません。予期しない動作やsegfaultが発生する場合があります。

小さい変数ポインタから大きい変数ポインタをキャストするときは、十分に注意する必要があります。このコードを考えてみましょう:

char* ptr = malloc (16);
ptr++;
uint64_t* uintPtr = ptr; // may cause an error, memory is not properly aligned

また、従う必要のある厳密なエイリアシングルールがあります。

于 2013-01-17T07:22:58.183 に答える
0

あなたはおそらく見る必要があります...スティーブサミットによって維持されているC-faq(以前はニュースグループに投稿されていました。つまり、当時の多くの最高のプログラマー、時には言語自体)。

要約版もありますが、これはおそらくもっと口当たりが良く、それでも非常に、非常に、非常に、非常に便利です。Cを使用する場合は、要約全体を読むことが必須であると私は信じています。

于 2013-01-17T08:28:02.830 に答える