C言語 | 構造体の宣言 | 構造体型の変換
構造体の値を別の構造体型へ直接変換することはできない。ただし、メモリレイアウトに互換性がある場合は、構造体へのポインターを変換できる。
構造体ポインターのキャスト
異なる構造体型の値を直接代入したり、キャストしたりすることはできない。
struct Point pt = { 10 , 100 };
struct Size sz = pt; /* コンパイルエラー */
次のように、物理的なレイアウトが同じ構造体を考える。
struct Point { int x , y; };
struct Size { int width , height; };
メンバー名と意味は異なるが、どちらも2つの int メンバーを持つ。ポインターをキャストすると、同じメモリを別のレイアウトとして解釈できる。
コード1
#include <stdio.h>
struct Point { int x , y; };
struct Size { int width , height; };
int main() {
struct Point pt = { 400 , 300 };
struct Size *psz = (struct Size *)&pt;
printf("&pt.x = %p : pt.y = %p\n" , &pt.x , &pt.y);
printf("&psz->width = %p , &psz->height = %p\n" , &psz->width , &psz->height);
printf("psz->width = %d : psz->height = %d\n" , psz->width , psz->height);
return 0;
}
この方法には注意が必要である。レイアウト、アラインメント、互換性に関する前提を誤ると、バグやデバッグの難しさにつながる。
互換性のある先頭部分を使うと、既存の構造体を限定的に拡張することもできる。
コード2
#include <stdio.h>
struct Color { char *name; int r , g , b; };
struct ColorEx { char *name; int r , g , b , a; };
void SetColor(struct Color *color) {
printf("%s r = %d : g = %d : b = %d\n" ,
color->name , color->r , color->g , color->b);
}
int main() {
struct Color color = { "Color" , 0xFF , 0 , 0 };
struct ColorEx colorEx = { "ColorEx" , 0 , 0xFF , 0 , 0xA0 };
SetColor(&color);
SetColor((struct Color *)&colorEx);
return 0;
}
ColorEx は Color と同じ順序で name、r、g、b を宣言し、その後に a を追加する。互換性のある先頭部分だけを読む既存関数には、キャストしたポインターを渡せる。より柔軟なAPIでは void * を利用できる。また、拡張可能な設計では構造体サイズをメンバーに保存し、利用可能なフィールドを判定する方法もある。