C言語のポインタがやっぱりよくわかってなかったんだけどこの記事を書いてるうちにその部分はわかりました! わーい!

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 = &pi;
    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_piyoPIYOを初期化しょうとしている。このとき、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はない。だから実体に代入しろと言われても無理ですと言われるのは当然だ。

アウトプットしたり他人に説明したりすると理解できるようになる、ということが実験で証明されていた気がする。 アウトプットってすごい。