【6日でできるC言語入門】scanf関数とポインタ変数

 C言語の入力処理でよく利用される関数が scanf です。scanf では、数値型や文字列型の入力時に「&(アドレス演算子)」を付けたり付けなかったりする書き方が登場します。なぜそのような違いが生まれるのか?その謎を解くカギは、「ポインタ」と「配列」の関係性にあります。ここでは、scanf とポインタ変数の関係を、図表や具体的なC言語プログラムを交えて徹底解説します。

1.scanf関数の基本とアドレスの意味

1.1. scanf関数と数値入力

 数値型変数へ値を入力する場合、scanf の第2引数には「変数のアドレス」を渡します。これは、scanf がそのアドレスに入力値を書き込むためです。

サンプルプログラム1:整数の入力と表示

プロジェクト/ファイル名: Lesson55_1/main.c

#include <stdio.h>

int main(void) {
    int age;
    printf("年齢を入力してください:");
    scanf("%d", &age); // &ageで「ageのアドレス」を渡す
    printf("あなたの年齢は%d歳です。\n", age);
    return 0;
}

実行結果

「SDLチェック」を「いいえ」にして、プログラムを実行します。

年齢を入力してください:25
あなたの年齢は25歳です。

解説
&age は「ageのアドレス(格納先)」を指します。
scanf はそのアドレスに、ユーザーが入力した値(ここでは25)を書き込みます。

1.2. scanf関数と文字列入力

文字列(char型配列)に値を入力する場合は、配列名だけでOKです。

サンプルプログラム2:名前(文字列)の入力と表示

プロジェクト/ファイル名: Lesson55_2/main.c

#include <stdio.h>

int main(void) {
    char name[20];
    printf("お名前を入力してください:");
    scanf("%s", name); // nameは配列の先頭アドレス
    printf("こんにちは、%sさん!\n", name);
    return 0;
}

実行結果

「SDLチェック」を「いいえ」にして、プログラムを実行します。

お名前を入力してください:さくら
こんにちは、さくらさん!

解説
name は配列の名前であり、「nameの先頭アドレス」を示します。
つまり、scanf("%s", name) と書くことは、scanf("%s", &name[0]) と同じ意味になります。

1.3. アドレス渡しの違いを表で比較

入力値の種類scanfの書き方アドレスの渡し方ポインタとの関係
整数/実数変数scanf("%d", &a);変数のアドレスを渡す&aはint *型のポインタ
文字列(配列)scanf("%s", s);配列の先頭アドレスsはchar *型のポインタ
配列の要素scanf("%d", &arr[0]);要素のアドレス&arr[0]はint *型のポインタ

2.ポインタと配列の関係性を図で理解

2.1. 配列名とアドレスの関係

配列名(例えば name)は、配列の「先頭アドレス」を意味します。
イメージ図

char name[5];
name ──→ | n | a | m | e | \0 |
           ↑
        &name[0]

ここで、name&name[0] も、実は同じアドレスを指しています。

2.2. scanf関数とポインタの型

  • 整数の入力例: scanf("%d", &n); ・・・&nはint型変数nのアドレス、つまりint型ポインタ(int *型)。
  • 文字列の入力例: scanf("%s", s); ・・・sはchar型配列の先頭アドレス、つまりchar型ポインタ(char *型)。

このように、どちらも「ポインタ」を渡している、という点がポイントです。

2.3. ポインタ変数とscanfの連携

サンプルプログラム3:ポインタ変数を利用した入力

プロジェクト/ファイル名: Lesson55_3/main.c

#include <stdio.h>

int main(void) {
    double num;
    double *p = # // numのアドレスをポインタ変数pに格納
    printf("小数を入力してください:");
    scanf("%lf", p); // ポインタ変数を使ってアドレスを渡す
    printf("入力した値は%.2fです。\n", num);
    return 0;
}

実行結果

「SDLチェック」を「いいえ」にして、プログラムを実行します。

小数を入力してください:3.14
入力した値は3.14です。

解説
scanf の第2引数は、double * 型(アドレス)を要求しています。ポインタ変数 p を渡すことで、numの格納先として利用できます。

3.注意点と実用例

3.1. 配列(文字列)の時だけアドレス演算子が不要な理由

配列名は「先頭アドレス」を直接意味するため、& を付けると「二重ポインタ」になってしまいます。
例:scanf("%s", &name); は正しく動作しない場合があるので注意しましょう。

3.2. 配列の各要素をscanfで入力する方法

サンプルプログラム4:複数要素の入力

プロジェクト/ファイル名: Lesson55_4/main.c

#include <stdio.h>

int main(void) {
    int data[3];
    printf("整数を3つ入力してください:\n");
    for (int i = 0; i < 3; i++) {
        scanf("%d", &data[i]);
    }
    printf("入力された値:%d, %d, %d\n", data[0], data[1], data[2]);
    return 0;
}

実行結果

「SDLチェック」を「いいえ」にして、プログラムを実行します。

整数を3つ入力してください:
7 8 9
入力された値:7, 8, 9

まとめ

scanf 関数の第2引数には「入力値を書き込むためのアドレス(ポインタ)」を渡す必要があります。
 数値型の場合は&を付けて変数のアドレスを、文字列(配列)の場合は配列名そのもの(=先頭アドレス)を指定するのがルールです。
 この仕組みと理由を理解することで、C言語の「ポインタ」と「配列」の根本的な関係性を自然に身につけることができます。今後のプログラム作成でも大いに役立つ知識となるでしょう。