MMGamesロゴ  MMGames
Twitterシェアボタン  Facebookシェアボタン   
しんで覚えるC言語
しんで覚えるC言語

&つけが必要な変数の正体

&付き変数の正体
前節では、変数のアドレスを表示する方法について説明しました。
このとき、変数名の前に&をつけて表示していましたが、
この書き方は、第6章でscanf関数を使う時にも使いました。

つまり、&付き変数は、アドレスを表示する時だけに使われるのではなくて、
さまざまな所で使われている書き方なのです。

この&付き変数の正体は、非常に単純です。
実は、&は変数のアドレスを求める演算子なのです。
+や×などと同じように、なんらかの計算を行うための記号なのです。

どんな変数でも、&演算子を使うと、変数のアドレスを求めることができます。
言い換えると、&演算子を使うと、その変数のメモリ上の番号を知ることができます。
すべては値渡しである
前項では、&演算子を使うと、変数のアドレスを知ることができると説明しました。
しかし、一体何のために、変数のアドレスを知る必要があるのでしょうか。
実は、これにはC言語の関数の仕組みが関係しているのです。

第12章では、自作関数と変数のスコープについて説明しました。
ここでは、引数という仕組みで、関数に数値を渡すことができると説明しました。
しかし、この引数で渡すことができるのは、あくまでも数値です。
関数を呼び出す時に変数を指定した場合には、
変数に記憶されている値が、呼び出された関数の実引数にコピーされます。

つまり、引数で関数に渡されるデータは、すべて数値であるということです。
この様に、引数で数値を渡す方式を、値渡しと呼ぶことがあります。

キーワード
【値渡し】

関数に単なる数値として情報を渡す方法。


ここで重要なのは、変数を実引数に指定しても、渡されるのは中身の数値である点です。
数値を受け取った関数では、それを元に色々計算して、結果を戻り値として返します。

通常はこれで問題ありませんが、もし、変数の中身を直接変更したい場合には困ります。
なぜなら、関数に渡されたのは変数の中身のコピーに過ぎないので、
そのコピーをいくら変更しても、呼び出し側の変数には何の影響もありません。

そこで、&演算子を使ってアドレスを求めて、そのアドレスの数値を渡します。
そうすれば、関数に呼び出し側の変数のアドレス、すなわちメモリ上の番号がわかり、
その番号のメモリを書き換えてやれば、呼び出し側の変数を書き換えられます。
つまり、これが変数のアドレスを知る必要がある理由です。

参照渡し
値渡しに対して、参照渡しという機能が存在する言語もあります。
これは、アドレスを渡す処理を自動的に処理してくれる機能です。
C言語では、値渡ししか出来ないのですが、
アドレスを渡すことを慣習的に参照渡しと呼ぶことがあります。

scanf関数で&をつける理由
前項で説明したことから考えれば、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番要素から入力されるので、最初からの文字列と結合されています。

つまり、配列名だけを渡している場合、確かに見た目には&をつけていないのですが、
実際に行っていることは、&付きの変数を指定しているのとまったく同じなのです。


本サイトについて

苦しんで覚えるC言語(苦C)は
C言語入門サイトの決定版です。
C言語の基本機能を体系立てて解説しており、
市販書籍と同等以上の完成度です。

第0部:プログラム概要編
  1. プログラムとは何か?
2章:プログラムの書き方
  1. 書き方のルール
  2. 書き方の慣習
  3. 練習問題2
3章:画面への表示
  1. 文字列の表示
  2. 改行文字
  3. 練習問題3
6章:キーボードからの入力
  1. 入力用の関数
  2. 入力の恐怖
  3. 練習問題6
9章:回数が決まっている繰り返し
  1. 繰り返しを行う文
  2. ループ動作の仕組み
  3. 練習問題9
10章:回数がわからない繰り返し
  1. 回数不明ループ
  2. 入力チェック
  3. 練習問題10
13章:複数の変数を一括して扱う
  1. 複数の変数をまとめて扱う
  2. 配列の使い方
  3. 練習問題13
20章:複数のソースファイル
  1. 最小限の分割
  2. 分割の定石
  3. 練習問題20

コメント
COMMENT

💬 コメント投稿欄を開く