Cのプログラミング指導において,「値渡しと参照渡し」についての,標準的な教え方は,次のようになると思います.
- 関数の呼び出しで,引数を受け渡しする方法として,「値渡し」と「参照渡し」があります.
- 値渡しをすると,関数の中で値を変更しても,呼び出す側に反映されません.
- 参照渡しをすると,関数の中で値を変更すると,呼び出す側に反映されます.
- Cでは通常,値渡しで引数を受け渡ししますが,変数の前に&をつけるか,配列変数名を渡すことで,参照渡しになります.
なのですが,最後の文は,このままで覚えると,誤解を招きかねません.仮引数の前に&をつけられます(ただしC++の構文として)し,仮引数は配列変数の形式で宣言できます.この誤解は,仮引数と実引数の取り違えによるものなので,これらの区別をきっちりすれば一応解決できますが,しかしそれでも,関数・変数・オブジェクトへの洞察を欠いたものとなっています.
私の授業での取り上げ方は,こうです.
- Cの関数の呼び出しで,引数を受け渡しすると,必ず「値渡し」となります.すなわち,関数の中で値を変更しても,呼び出す側に反映されません.
- Cに限らず,引数を受け渡す方法として,「参照渡し」というのもあります.参照渡しをすると,関数の中で値を変更すると,呼び出す側に反映されます.
- Cで「参照渡し」を実現するには,ポインタ値を受け渡しします.呼び出す側の,実引数*1には,変数の前に&をつけるか,配列変数名を書くといいでしょう.呼び出される側の,仮引数は,ポインタ変数とします.関数の中で,仮引数の指し示す先の値を変更すると,実引数に反映されます.
説明のポイントは,ありとあらゆる説明手段を使って仮引数と実引数を区別することと,参照渡しでは「仮引数の値」ではなく「仮引数が指し示すオブジェクト」に着目する(してもらう)ことです.
ここで,「仮引数(ポインタ変数)」と「仮引数が指し示すオブジェクト(たいていポインタではない)」を区別するために,次の補足を記しておきましょう.
- ポインタ値を受け渡ししたとき,その仮引数の値を変更すること,例えばp++;とか書くこともできます.そしてそれをしても,pが指し示している先のオブジェクト(実引数で,おそらく配列領域)の値は変更されません.
この補足には,さらに補足がつきます.覆面座談会の形式で,書いてみます.
- ということで,ポインタ値を受け渡しするのも,「値渡し」なのです.
- いや,ポインタ値を受け渡しするのは,「値渡し」の意味合いと,「参照渡し」の意味合いの,両方があるということでしょう.
- 参照渡しとして使う限り,関数の中のポインタ変数でp++;とするのは,まずいと思います.
- いや例えば,文字列を引数にとって,あるところまで先頭から見ていって(ループの中にp++;が入っていたりして),条件を満たしたところでそこに*p='\0';として,文字列をそこまでにする処理を,書きたいことがあるのではないでしょうか.
これらを踏まえて,「値渡しと参照渡し」をどう教えるかですが,参照渡しを教える前に,ポインタをしっかり理解してもらうことが前提になるでしょう.ポインタを教える前に,配列は必須です.ということで私は,配列→ポインタ→関数という流れで,授業の中盤を組み立てています.
*1:私の先日の授業に真剣に耳を傾けた人は,「関数を呼び出す側に書く引数 = 実引数」,「関数が呼び出される側に書く引数 = 関数定義のカッコの中に書く変数 = 仮引数」の対応付けができたと思います.まだの人は,この注釈を見た機会に,暗記してください.