お恥ずかしながら、C言語はちゃんと勉強したことがなくポインタについては知識レベルでしか理解しておらず、もう少し慣れておきたいなと思い、今回改めてちゃんと調べてみました。
あ、保身のために注意書きですが、他のJava, JavaScript等の言語を書くときはちゃんとメモリ意識してプログラミングしてるので概念はちゃんと理解してますよ。
ただ、「*」をつけたり「&」をつけたりする書き方が直感的によく分からなかったので改めて調べた次第です。
そのため、この記事は高級言語を理解していて、C言語は理解してない人向けの記事です。
実行したコード
多分、「日本語ガチャガチャ書かれてもよく分からないからソース見せろ!」ってのがプログラマ諸君の総意だと思うので、ソースでまとめました。
実際の値、ポインタ、ダブルポインタに対して、「&」「*」「**」を付けた時にどの値が表示されるかを確認しています。
それぞれ、”%d”で整数の実際の値、”%p”でポインタの値を出力しています。
#include <stdio.h> int main(void) { int val = 10; // 実際の値 int *p = &val; int **pp = &p; printf("(&val) data:%d, pointer:%p\n", &val, &val); printf("(val) data:%d, pointer:%p\n", val, val); printf("(*val) data:%d, pointer:%p\n", *val, *val); // NULL参照になるためコメントアウト // printf("(**val) data:%d, pointer:%p\n", **val, **val); printf("(&p) data:%d, pointer:%p\n", &p, &p); printf("(p) data:%d, pointer:%p\n", p, p); printf("(*p) data:%d, pointer:%p\n", *p, *p); printf("(**p) data:%d, pointer:%p\n", **p, **p); printf("(&pp) data:%d, pointer:%p\n", &pp, &pp); printf("(pp) data:%d, pointer:%p\n", pp, pp); printf("(*pp) data:%d, pointer:%p\n", *pp, *pp); printf("(**pp) data:%d, pointer:%p\n", **pp, **pp); return 0; }
実行結果
「*」でプリミティブな値に近づき、「&」で離れていくっていうざっくりとした覚え方で良さそうですね。
(&val) data:XXX, pointer:0x50a2f0 (val) data:10, pointer:XXX (*val) data:XXX, pointer:XXX (&p) data:XXX, pointer:0x50a328 (p) data:XXX, pointer:0x50a2f0 (*p) data:10, pointer:XXX (**p) data:XXX, pointer:XXX (&pp) data:XXX, pointer:0x50a360 (pp) data:XXX, pointer:0x50a328 (*pp) data:XXX, pointer:0x50a2f0 (**pp) data:10, pointer:XXX
あと、変数を定義したときと同じ数の「*」をつけると実際の値を参照できると暗記するのもアリかもですね。
まぁ引数として関数に値を渡した時に訳わからなくなるかもしれませんが…。
clang実行に使用したのは以下のサイト
*valがエラーにならないのは少し意外でした。
int型整数である「10」をアドレスと解釈して参照しにいこうとするらしいです。
たしかに、それなら少なくとも「10」の場合はエラーにはならないか🤔
余談
C言語でもメモリの確保の部分は隠蔽化されていて、Java, JavaScriptあたりと同じく”なんかうまいことやってくれてる”ので、他の勉強よりも優先度上げてCを勉強する必要はあるのかなと疑問に思ってしまいます。
まぁメモリの解放を自分でやる感覚は大事な気もしますし、基礎を理解するのは大事なんですが、時間もリソースなんや。
でもベースの技術を学ぶのは楽しいですね。
Cにおける変数の型を調べている途中で、1バイト=8ビットが正式に決定されたのが2008年と知ってびっくりしました。
charが1バイトである理由とか、int32_tみたいな謎な型がある理由とか、intとlong intのバイト数がイコールの理由とか、調べると面白いです。
今度は「Javaのプリミティブ型はCに準拠してるのか???」と思って見比べてみると、Javaのcharは2バイトだったり、longがCのlong long intとバイト数がイコールだったりと、一見するとイケてないように見えるものが結構ありますw
人間側の都合と歴史ですねw
論理的で整理されたものほど、時代における最適解を選んでも、それが過去になった時に最適ではなくなるっていう証明に繋がるのが面白いところです。
まぁ今回はこの辺で。