変数の宣言につける*と間接参照演算子
さ〜て今回もプログラミング実習の演習課題を片付けるぞ
ポインタ?簡単っていうwwwwwwこのくらいできて当たり前っていうwwwwwwwwwwwww
じゃあ俺提出してからゼロロク撮りに行くから(棒読み)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <stdio.h> typedef struct { int height; int weight; int age; } MEMBER; int main(void) { MEMBER d[3] = { {163, 90, 53}, {165, 75, 60}, {170, 74, 24} }; MEMBER *p = d; int i; for (i = 0; i < 3; i++, p++) printf("%d %d %d\n", p->height, p->weight, p->age); return 0; } |
意外と早くできたな〜(嬉しい誤算)
コンパイル通るし、実行結果も誤りがないし、問題の要件満たしてるからこれは間違いなく正解だな!
先生「違います」
ファッ!?
一体なにが違ったのか・・・
ゼロロク撮りたさに手早く問題を解いたせいで自分で考えるように言われてしまいました。
〜20分後〜
ぼく「もう十分だ・・・もう十分考えたよ・・・」
あまり時間を伸ばすとゼロロク撮りに行けないので、悔しいですが降参することにしました。
ゼロロクは珍しいから仕方ないよな。
それで気になるのは間違っていた箇所について。
コンパイルが通っていた上に実行結果も正解と全く同じだったので検討がつくわけ無いし・・・
間違ってたと言われたのはここでした。
1 |
MEMBER *p = d; |
その後左辺と右辺の型が違うと言われました。
右辺には値が来ると言われました。
いや、右辺はアドレスじゃないとダメなんだが・・・
この日はなんとかして誤解を訂正してもらい、残りの問題をさっさと解いて無事にゼロロクを撮れたのでハッピーエンドでした。
しかし、なぜポインタ変数の初期化に値を入れようという誤解が生じてしまうのか、そこが気になりました。
大学の授業でのポインタ
教科書ではポインタ変数の宣言をこのように書いてあります。
1 |
int *p; |
そして
1 |
int* p; |
みたいな書き方(C++でよく見る書き方)もあるわけですが、この書き方は推奨されないとはっきりと授業の中で学生に対して教えていました。
(C++書いてる僕からすればあまり同意できませんが)
それと、問題の中では使う変数を指定してくることも多いわけですが、その際に初期化ではなく代入でないといけないという細かい決まりもありました。
今までやってきた問題は「ポインタ変数pを宣言し、変数aのアドレスを代入する」みたいな指定がありました。
しかし、今回の問題は偶然にも代入しろという指定が無かったので、わざとコードが短くなる初期化で済ませたわけです。
つまり、今まで初期化を使わないことが多く、ポインタ変数を何らかのアドレスで初期化するコードを提出した人があまりいなかったというわけですね。
よくある誤解
というわけで大学では
1 |
int *p; |
のように*変数名
としているわけですが、これにはある注意事項があるということを授業では全く触れていませんでした。(というか教える立場の人も勘違いしていた)
ポインタ変数は間接参照演算子*
でポインタが示している変数を参照できます。
1 2 3 4 |
int n = 5; int *p; p = &n; *p = 100; |
*p = 100
のように変数名に*
をつけているのですが、これどこかで見ませんでしたか?
1 |
int *p; |
1 |
*p = 100; |
上のコードはポインタ変数の宣言で、下はポインタ変数に間接参照演算子でポインタが示している変数に対して代入を行っているだけですが、両方とも*p
と書いています。
そのため、下のコードの=
の後は値が来るのか、それともアドレス(ポインタ変数)が来るのか、となった時にどちらが正解なのか悩んでしまうわけです。
1 |
int *p = |
もし、アドレスを入れるポインタ変数ではなく、値を入れる変数であれば値が来ます。
1 |
int n = 1; |
それならアドレスを入れるポインタ変数の場合はアドレスが来るだろう、と考えることができます。(こっちが正解です)
1 2 |
int n = 1; int *p = &n; |
一方で*p = 100
というように書くのだから、変数の宣言時でも同じように値を入れるとした人が結構います。本当に結構いましたよ。
1 |
int *p = 100; |
C言語では
1 |
int *p; |
のように変数名に*
つけることが多いので、ポインタ変数の宣言に使う*
と間接参照演算子の*
は違うのできちんと区別しましょう!
つまり
1 |
int* p; |
のように型に*
をつけるのにはきちんとした意味あるということですね。
ただし、この書き方すると
1 |
int* p1, p2; |
のように1行で2つの変数を宣言した時に、p1
はint *
だけど、p2
はint
になってしまうことを勘違いしやすいという欠点もありますよ。