私は最近、Cプログラミングの初心者へのポインタを説明することができて、次の困難に遭遇しました。ポインタの使用方法をすでに知っている場合は、まったく問題に見えないかもしれませんが、次の例を明確に考えてみてください。
int foo = 1;
int *bar = &foo;
printf("%p\n", (void *)&foo);
printf("%i\n", *bar);
まったくの初心者にとって、出力は意外かもしれません。2行目では、* barを&fooと宣言したばかりですが、4行目では、* barは実際には&fooではなくfooであることがわかります。
混乱は、*シンボルのあいまいさから生じていると言えるかもしれません。2行目では、ポインターの宣言に使用されています。4行目では、ポインターが指す値をフェッチする単項演算子として使用されています。二つの違うことですよね?
ただし、この「説明」は初心者にはまったく役立ちません。微妙な食い違いを指摘することで、新しいコンセプトを紹介しています。これはそれを教えるための正しい方法ではありません。
それで、カーニハンとリッチーはそれをどのように説明しましたか?
単項演算子*は間接演算子または逆参照演算子です。ポインターに適用されると、ポインターが指すオブジェクトにアクセスします。[…]
ポインターipの宣言は、
int *ip
ニーモニックとして意図されています。それは式*ip
がint であると言います。変数の宣言の構文は、変数が出現する可能性のある式の構文を模倣しています。
int *ip
「*ip
が返されますint
」のように読む必要がありますか?しかし、なぜ宣言後の割り当てがそのパターンに従っていないのですか?初心者が変数を初期化したい場合はどうなりますか?int *ip = 1
(読み:*ip
返されるint
とint
ある1
)期待通りに動作しません。概念モデルは首尾一貫していないようです。ここで何か不足していますか?
*
、宣言では「ポインターを宣言する」を意味するトークンであり、式では逆参照演算子であり、これら2つは偶然同じシンボルを持つ異なるものを表すという事実を常に主張しました(乗算演算子と同じ-同じ記号、異なる意味)。混乱を招きますが、実際の状況と異なるものはさらに悪化します。
int* bar
スターを実際には型の一部であり、識別子の一部ではないことをより明確にするようにそれを書くかもしれません。もちろん、これはのような直感的でないものでさまざまな問題に遭遇しint* a, b
ます。
*
コンテキストに応じて2つの異なる意味を持つことができます。同じ文字でも、単語によって発音が異なるのと同じように、多くの言語を話すことを学ぶのは難しいです。すべての概念/操作に独自の記号がある場合、より大きなキーボードが必要になるため、意味がある場合は記号をリサイクルします。
int* p
あなたの学生を警告しながら、)に対するポインタが関与しているときに、同じ行に複数の宣言を使用しました。生徒がポインタの概念を完全に理解したら、int *p
is構文は同等であることを生徒に説明し、複数の宣言の問題を説明します。