Heppoko Binbo Yarou no Newbie Nikki

旧題: へっぽこびんぼう野郎のnewbie日記 #vZkt8fc6J

たわむれにC言語のポインタで遊んでみた

はじめに

C言語のポインタって、違う意味の操作なのに * を使いまわしてるからわかりにくくなっているんだと思う。

あと「ポインタ」っていう名称もよくない気がする。ポインタっていうと普通矢印を思い浮かべるので、「ポインタに代入」っていうと「矢印に代入」という意味っぽくなって混乱する。メモリアドレス型って呼んでおけば「このメモリアドレス型変数xの指し示す先(ポインタ)は2」みたいに言えたのに…って思う。

なので、人に教えるときに無理に「ポインタ」という言葉と結びつけて教えると矢印地獄にハマる。

↓こう書ければいいのにって思った。(たぶん世界一わかりやすい)

#include <stdio.h>

int main() {
    int k = 1;
    int{pointer} m = (address)k;
    int{pointer}{pointer} e = (address)m;
    printf("%d\n", (value)m);
    (value)m = 4;
    printf("%d\n", (value)m);
    return 0;
}

ちなみに↓と等価

#include <stdio.h>

int main() {
    int k = 1;
    int* m = &k;
    int** e = &m;
    printf("%d", *m);
    *m = 4;
    printf("%d", *m);
    return 0;
}

個人的に int *x = 4 みたいに、xのすぐ左にアスタリスクつけてる慣習も嫌だなーって思う。

int* x の方がいいと思う。だって int *x だと *x っていう変数っぽく見える。 int* の方が重要視されるべきだから、 int* x のほうがよくない?

ポインタとして使うときも x = &y みたいに使うじゃん。まあ今更変えようがないだろうしどっちでもいいけど……

あそんでみた

#include <stdio.h>

void change(int* b, int v) {
    *b = v;
}

int main () {
    // 普通に変数代入
    int a = 1;

    // ポインタを宣言するアスタリスク
    int* x;
    // アスタリスクは↓こうつけてもかまわない。意味はおなじ。こちらの方が慣習的に多いイメージ
    int *c;
    // ポインタとして使うときはアスタリスクをつけない
    c = x;

    int* y = &a;  // &によって、変数aのアドレスを取得する
    x = y;

    printf("-------------- ポインタを表示してみる ------------\n");
    printf("&aの値: %p\n", &a);
    printf("yの値: %p\n", y);
    printf("xの値: %p\n", x);
    printf("cの値: %p\n", c);  // ポインタ変数xを宣言したときの初期化してない値が入ったままなのでここだけ値が違う。

    printf("-------------- ポインタが指す変数の値を取得してみる。このアスタリスクとポインタ宣言するときのアスタリスクは意味が異なるので混同しないように注意。 ------------\n");
    printf("*yの値: %d\n", *y);

    printf("-------------- ポインタのポインタ ------------\n");
    // ポインタへのポインタを宣言する
    int** z = &y;  // yはすでにポインタなので、&yはこのポインタのアドレスとなる。なのでポインタのポインタに代入できる。
    printf("zの値: %p\n", z);
    printf("*zの値: %p\n", *z);
    printf("**zの値: %d\n", **z);

    printf("-------------- 左の式と右の式の両方でアスタリスクを使ってみた。[再掲] *zのアスタリスクによって「ポインタのポインタ」が指す変数の値(=つまり単なるポインタ)を取得しているからpに代入できていることに注意。左式の*と右式の*を同じ意味で捉えると詰む ------------\n");
    // &aであるようなポインタ*zを、pに保存する
    int* p = *z;
    printf("*pの値: %d\n", *p);

    printf("-------------- 実際の値を変更するためのアスタリスク。ポインタ宣言じゃないよ! --------------\n");
    *y = 4;
    printf("yの値: %p\n", y);
    printf("*yの値: %d\n", *y);
    printf("**zの値: %d\n", **z);

    printf("-------------- ポインタのポインタの、実際の値の実際の値に3を代入した -------\n");
    **z = 3;
    printf("*pの値: %d\n", *p);
    printf("**zの値: %d\n", **z);
    printf("aの値: %d\n", a);

    printf("-------------- 関数の場合もそれはかわらない -------\n");
    // 以下はすべてアドレスを渡している
    change(&a, 0);
    printf("aの値: %d\n", a);
    change(y, 5);
    printf("aの値: %d\n", a);
    change(*z, 9);
    printf("aの値: %d\n", a);

    printf("-------------- もっと遊んでみる -------\n");
    int* *r = &*&y;
    **&*r = 10;
    printf("rの値: %p\n", r);
    printf("**rの値: %d\n", *&**r);
}