日々の記録

SNS は無限に時間を溶かすのでやらないことにした

構造体のポインタメモ

構造体のメンバのポインタについて、特に意識せず使っていたので具体的にどのようにアドレスが確保されているのか確認した際のメモ。そもそも構造体のポインタで、メンバがさらにポインタの場合って具体的にどのようにアドレスが確保されて、どうやってアクセスすれば良いのだろうか。
まずアドレスの確保についてこんな感じでコードを書いてみる。

#include <stdio.h>

typedef struct {
  int intVal;
  char charVal;
  char *strVal;
} Struct;

int main(int argc, char const *argv[]) {

  Struct data;

  data.intVal = 1;
  data.charVal = 'c';
  data.strVal = "strVal";

  printf("%s\n", "Show individual pointers and values.\n----------------");
  printf("&data.intVal : %p | data.intVal  : %d\n", &data.intVal, data.intVal);
  printf("&data.charVal: %p | data.charVal : %c\n", &data.charVal, data.charVal);
  printf("&data.strVal : %p | data.strVal  : %s\n\n", &data.strVal, data.strVal);

  Struct *p_data = NULL;
  p_data = &data;

  data.intVal = 2;
  data.charVal = 'd';
  data.strVal = "strVal2";

  printf("%s\n\n", "p_data = &data..." );

  printf("%s\n", "Enter an address of struct to another pointer.\n----------------");
  printf("&data        : %p\n", &data);
  printf("&data.intVal : %p | data.intVal : %d\n", &data.intVal, data.intVal);
  printf("&data.charVal: %p | data.charVal: %c\n", &data.charVal, data.charVal);
  printf("&data.strVal : %p | data.strVal : %s\n\n", &data.strVal, data.strVal);

  printf("%s\n", "Show the struct pointer's value.\n----------------");
  printf("&p_data         : %p | p_data         : %p\n", &p_data, p_data);
  printf("&p_data->intVal : %p | p_data->intVal : %d\n", &p_data->intVal, p_data->intVal);
  printf("&p_data->charVal: %p | p_data->charVal: %c\n", &p_data->charVal, p_data->charVal);
  printf("&p_data->strVal : %p | p_data->strVal : %s\n\n", &p_data->strVal, p_data->strVal);

  return 0;
}

実行してみるとこんな感じ。

Show individual pointers and values.
----------------
&data.intVal : 0x7fffcdbecfb0 | data.intVal  : 1
&data.charVal: 0x7fffcdbecfb4 | data.charVal : c
&data.strVal : 0x7fffcdbecfb8 | data.strVal  : strVal

p_data = &data...

Enter an address of struct to another pointer.
----------------
&data        : 0x7fffcdbecfb0
&data.intVal : 0x7fffcdbecfb0 | data.intVal : 2
&data.charVal: 0x7fffcdbecfb4 | data.charVal: d
&data.strVal : 0x7fffcdbecfb8 | data.strVal : strVal2

Show the struct pointer's value.
----------------
&p_data         : 0x7fffcdbecfa8 | p_data         : 0x7fffcdbecfb0
&p_data->intVal : 0x7fffcdbecfb0 | p_data->intVal : 2
&p_data->charVal: 0x7fffcdbecfb4 | p_data->charVal: d
&p_data->strVal : 0x7fffcdbecfb8 | p_data->strVal : strVal2

ここから分かることは、、、

  • 構造体変数のアドレスは、その構造体内の最初のメンバ変数のアドレスと同じ
  • 構造体変数の各メンバは、前のメンバ変数の隣のアドレスを順次確保する

で、ややこしいのは、構造体型のポインタ変数(ここではStruct *p_data)を宣言した際に、メンバ変数がさらにポインタだった場合のアクセス。構造体変数がポインタの場合、->を使えばいいのだけど、さらにアクセスしたいメンバ変数がポインタだと、どうやってアクセスすれば良いのかあまり記載がない気がする。
実はSocketプログラムで古いコードをコンパイルした際に、gethostbyname()を使っている部分でgetnameinfo()を使えというコンパイルエラーに出くわした。gethostbyname()hostent構造体を返してくれるのだけれど、これがlsb4.x以降で非推奨となったようで、このコードを回収したいのだけれど、なるべく修正範囲を少なくしたいので「具体的にhostentの中って何が入ってたっけ?getnameinfo()で取得した値はhostentにぶち込めないのかしら?」という疑問にぶち当たった。ところがhostent構造体変数はポインタで、さらにこのポインタのメンバもポインタ変数ときた。このパターンはよくあるけれど、実際にアクセスしようとすると、どうやってアクセスすれば良いものやら。。。
ということで、前述のコードの実行結果から、以下のようなことが読み取れた。

  • 構造体型のポインタ変数へのアクセスは、&ポインタ変数 でアクセスできる(ここではStruct *p_dataで宣言されており、&p_dataでアクセスできる)
  • 構造体型のポインタ変数内で、メンバ変数へのアクセスは、構造体変数->メンバ変数でアクセスできる(ここではp_data->strValなど)
  • 構造体型のポインタ変数内で、メンバのポインタ変数へのアクセスは、&構造体変数->メンバ変数でアクセスできる(ここでは&p_data->strVal)