C言語の簡単なプログラムを作って構造体とポインタの挙動を調べているんだけど、やっぱりよくわからないところが出てきた。
まず、こう書いてみる。
#include <stdio.h> #include "test.h" #include <string.h> #include <stdlib.h> int zero = 0; typedef struct{ int *x; } X; typedef struct piyoo{ X *piyo; struct piyoo *next_piyo; } PIYO; PIYO *make_piyo() { PIYO pi, *piyopiyo; piyopiyo = π piyopiyo = malloc(sizeof(PIYO)); piyopiyo->piyo = malloc(sizeof(X)); piyopiyo->piyo->x = &zero; piyopiyo->next_piyo = NULL; return piyopiyo; } void delete_piyo(PIYO *piyopiyo) { free(piyopiyo->piyo); free(piyopiyo); } int main() { PIYO *piyopiyo = make_piyo(); printf("piyo %d\n", *(piyopiyo->piyo->x)); // delete_piyo(piyopiyo); free(piyopiyo->piyo); int y = 3; piyopiyo->piyo->x = &y; printf("3 %d\n", *(piyopiyo->piyo->x)); free(piyopiyo); return 0; }
何をしているのかというと、自己参照構造体PIYO
を作り、PIYO
はメンバとして構造体X
を持っている。このX
の中身はint型のポインタだ。
で、いま関数make_piyo
でPIYO
を初期化しょうとしている。このとき、PIYOの唯一の実体であるpiyopiyo->piyo->x
の中身を0にしようとしている。
上のコードでは、piyopiyo->piyo->x
にグローバル変数zero
のアドレスを格納する、というやり方をとっている。
ところが、次のようにコードの該当部を変更するとセグフォする。
*(piyopiyo->piyo->x) = x;
関数make_piyo
では、PIYO構造体としての実体のpi
を作り、そのアドレスをpiyopiyo
に格納している。で、実体のpi.piyo
が指し示しているポインタx
の指し示す実体の内容を変えたかった。
本当はわからないので記事を投稿して反応を待とうと思ったんだけど、書いているうちに原因がわかってしまった。
PIYO
構造体pi
が保持しているのはあくまでint型へのポインタ(つまり、アドレスを格納する場所)のみであって、そこに実体のintはない。だから実体に代入しろと言われても無理ですと言われるのは当然だ。
アウトプットしたり他人に説明したりすると理解できるようになる、ということが実験で証明されていた気がする。 アウトプットってすごい。