前節では、変数のアドレスを表示する方法について説明しました。
このとき、変数名の前に&をつけて表示していましたが、
この書き方は、
第6章でscanf関数を使う時にも使いました。
つまり、&付き変数は、アドレスを表示する時だけに使われるのではなくて、
さまざまな所で使われている書き方なのです。
この&付き変数の正体は、非常に単純です。
実は、&は変数の
アドレスを求める演算子なのです。
+や×などと同じように、なんらかの計算を行うための記号なのです。
どんな変数でも、&演算子を使うと、変数のアドレスを求めることができます。
言い換えると、&演算子を使うと、その変数のメモリ上の番号を知ることができます。
前項では、&演算子を使うと、変数のアドレスを知ることができると説明しました。
しかし、一体何のために、変数のアドレスを知る必要があるのでしょうか。
実は、これにはC言語の関数の仕組みが関係しているのです。
第12章では、自作関数と変数のスコープについて説明しました。
ここでは、引数という仕組みで、関数に数値を渡すことができると説明しました。
しかし、この引数で渡すことができるのは、あくまでも数値です。
関数を呼び出す時に変数を指定した場合には、
変数に記憶されている値が、呼び出された関数の実引数にコピーされます。
つまり、引数で関数に渡されるデータは、すべて数値であるということです。
この様に、引数で数値を渡す方式を、
値渡しと呼ぶことがあります。
【値渡し】
関数に単なる数値として情報を渡す方法。
ここで重要なのは、変数を実引数に指定しても、渡されるのは
中身の数値である点です。
数値を受け取った関数では、それを元に色々計算して、結果を戻り値として返します。
通常はこれで問題ありませんが、もし、変数の中身を直接変更したい場合には困ります。
なぜなら、関数に渡されたのは変数の中身のコピーに過ぎないので、
そのコピーをいくら変更しても、呼び出し側の変数には何の影響もありません。
そこで、&演算子を使ってアドレスを求めて、そのアドレスの数値を渡します。
そうすれば、関数に呼び出し側の変数のアドレス、すなわちメモリ上の番号がわかり、
その番号のメモリを書き換えてやれば、呼び出し側の変数を書き換えられます。
つまり、これが変数のアドレスを知る必要がある理由です。
値渡しに対して、参照渡しという機能が存在する言語もあります。
これは、アドレスを渡す処理を自動的に処理してくれる機能です。
C言語では、値渡ししか出来ないのですが、
アドレスを渡すことを慣習的に参照渡しと呼ぶことがあります。
前項で説明したことから考えれば、scanf関数で&をつける理由がわかると思います。
scanf関数は、キーボードからの入力を行い、変数に記憶する関数です。
しかし、前項で説明した通り、C言語では値渡ししかできません。
つまり、関数には変数に記憶された値のコピーしか渡すことができません。
これでは、変数に新たな値を記憶することが出来なくなってしまいます。
その場合、前項で説明したように、変数のアドレスを数値として渡してやれば、
関数の側で、そのアドレスにデータを記憶することができるわけです。
これが、scanf関数で変数に&をつける理由です。
しかし、疑問なのは、文字列を入力する時は、配列名の前に&をつけなかったことです。
これは、前節で説明したように、
配列名は配列の最初の要素のアドレスを表しています。
したがって、配列名を渡せば、配列の場所がわかるため、&は不要です。
つまり、ある意味配列名でなくても、配列のアドレスさえ指定すれば良いのです。
たとえば、次のようにすれば、配列名でなくても入力ができます。
#include <stdio.h>
int main(void)
{
char str[256];
scanf("%s", &str[0]); /* 0番の要素のアドレス */
printf("%s\n", str);
return 0;
}
このプログラムの実行結果は、次の通りになります。
MARIO キーボードから入力した文字列
MARIO
このプログラムは、配列名の代わりに、要素0番のアドレスを指定しただけです。
どちらもまったく同じことを意味しているので、問題ありません。
さらに、次のようにすれば、配列の途中から入力させることすらできます。
#include <stdio.h>
int main(void)
{
char str[256] = "DRAGON";
scanf("%s", &str[6]); /* 6番の要素のアドレス */
printf("%s\n", str);
return 0;
}
このプログラムの実行結果は、次の通りになります。
QUEST キーボードから入力した文字列
DRAGONQUEST
このプログラムでは、初期化の段階で配列に DRAGON の文字列を代入しています。
つまり、配列の0~5番までには文字がすでに代入されています。
そこで、scanf関数に6番要素のアドレスを指定して入力すれば、
文字列は6番要素から入力されるので、最初からの文字列と結合されています。
つまり、配列名だけを渡している場合、確かに見た目には&をつけていないのですが、
実際に行っていることは、&付きの変数を指定しているのとまったく同じなのです。