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