へっぽこ社会人4年生がプログラミングを頑張る

へっぽこ社会人4年目がプログラミング系統を中心に書きたいことをつらつらと書きます

Cの構造体へのポインタのキャストを考える

Cでソケットプログラミングをする時なんかに、 構造体struct sockaddr_inbindaccept, connectなどの関数に渡す際に、 struct sockaddr*にキャストして渡します。

正直、僕にとっては、構造体へのポインタのキャストって、 イメージしづらい と感じます。 Javaとかのクラスであれば、多相性とかのイメージなんでしょうけれど、 Cの構造体はあくまでデータ構造の定義ですので...

例えば、こんなコードを考えてみます。

#include <stdio.h>

struct hoge {
  double a;
  int b;
};

struct piyo {
  int age;
  double height;
  double weight;
};

void printHoge(struct hoge *h)
{
  printf("a: %lf\n", h -> a);
  printf("b: %d\n", h -> b);
}

void printPiyo(struct piyo *p)
{
  printf("age: %d\n", p -> age);
  printf("height: %lf\n", p -> height);
  printf("weight: %lf\n", p -> weight);
}


int main(int argc, char **argv)
{
  struct hoge h = {3.14, 10};
  struct piyo p = {22, 169.5, 54.3};

  printHoge(&h);
  putchar('\n');
  printHoge((struct hoge*)&p);

  putchar('\n');
  putchar('\n');
  putchar('\n');

  printPiyo((struct piyo*)&h);
  putchar('\n');
  printPiyo(&p);

  return 0;
}

それぞれprintHogeprintPiyoで、 構造体struct hogestruct piyoの内容を表示させます。 それぞれ、引数は構造体へのポインタを指定しています。 そこで、struct hogeの変数hstruct piyoの変数pを それぞれキャストして渡してみます。 すると、実行結果は以下のようになりました。

$ ./struct
a: 3.140000
b: 10

a: 0.000000
b: 0



age: 1374389535
height: 0.000000
weight: -322.000000

age: 22
height: 169.500000
weight: 54.300000
$ 
訳が分からん!!

まあ、データ構造が全然違うので、当たり前かもしれませんが... おそらく、データ構造が全然違うものをキャストして使うこと自体が誤りなのでしょう。

今度は、少しデータ構造を似せてみます。 struct hogestruct piyoのデータ構造に共通部を持たせてみます。

#include <stdio.h>
#define SIZE 65536

struct hoge {
  char buf[SIZE];
  int  hogera;
};

struct piyo {
  char name[SIZE];
  int  age;
  double height;
  double weight;
};

void printHoge(struct hoge *h)
{
  printf("buf:\t%s\n", h -> buf);
  printf("hoge:\t%d\n", h -> hogera);
}

void printPiyo(struct piyo *p)
{
  printf("name:\t%s\n",    p -> name);
  printf("age:\t%d\n",     p -> age);
  printf("height:\t%lf\n", p -> height);
  printf("weight:\t%lf\n", p -> weight);
}


int main(int argc, char **argv)
{
  struct hoge h = {"hoge", -1};
  struct piyo p = {"ponta", 20, 170.4, 61.2};

  printHoge(&h);
  putchar('\n');
  printHoge((struct hoge*)&p);

  putchar('\n');
  putchar('\n');
  putchar('\n');

  printPiyo((struct piyo*)&h);
  putchar('\n');
  printPiyo(&p);

  return 0;
}

struct hogestruct piyoの先頭の構造を、charの配列と、 intをメンバ変数に共通させてみます。 このコードの実行結果は以下の通りです。

$ ./struct2
buf:	hoge
hoge:	-1

buf:	ponta
hoge:	20



name:	hoge
age:	-1
height:	0.000000
weight:	0.000000

name:	ponta
age:	20
height:	170.400000
weight:	61.200000
$ 

今度はcharへの配列とintのメンバ変数の内容が表示されました。 struct hogeの変数をprintPiyo関数にstruct piyo*型にキャストして実行してみたら、 自分の環境(FreeBSD 10.2-RELEASE-p24, clang version 3.4.1)では、doubleのメンバ変数の中身は0.000000と表示されました。 おそらく、環境依存だと思いますが...

とりあえず、なんとなく挙動は確認してみたのですが、 やはり勉強不足なので、もうちょっと調べてみようかなと思います。 そんなこんなで、今回はここまで。


2022/4/20 追記

構造体へのポインタのキャストについて、筆者が理解した内容をこちらの記事にまとめました。