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;
}

ColorExColor と同じ順序で namergb を宣言し、その後に a を追加する。互換性のある先頭部分だけを読む既存関数には、キャストしたポインターを渡せる。より柔軟なAPIでは void * を利用できる。また、拡張可能な設計では構造体サイズをメンバーに保存し、利用可能なフィールドを判定する方法もある。