あるタイプへのポインターが別のタイプのポインターに変換される可能性があることを私は知っています。私は3つの質問があります:
- ポインターを型キャストする際に留意すべきことは何ですか?
- 結果のポインタで発生する可能性のある例外/エラーは何ですか?
- 例外/エラーを回避するためのベストプラクティスは何ですか?
よく書かれたプログラムは通常、ポインタ型キャストをあまり使用しません。たとえば、 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 *)▭
p->whatami = POLY_RECTANGLE; // a way to simulate polymorphism ...
process_poly ( p );
ただし、この場合は、C++を直接使用する方がよいかもしれません。
ポインタ型キャストは、開発を開始する前に、プログラム分析の一部である明確に決定された状況に注意深く使用する必要があります。
ポインター型キャストの潜在的な危険性
s1 *p = (s1 *)&s2;
:サイズと配置に依存すると、エラーが発生する可能性があります(しかし、公平を期すために、熟練したCプログラマーは上記の間違いを犯しません...)
ベストプラクティス
プレーン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
また、従う必要のある厳密なエイリアシングルールがあります。