C言語基礎|配列の要素に値を読み込む

配列の基本(要素と添字、初期化)が分かったら、次は一気に実用感が出ます。
そう、キーボード入力で配列にデータを入れるやつです。

たとえば、

  • 5人分の身長
  • 7日分の歩数
  • センサーの測定値
  • テストの点数

みたいに「同じ型のデータをまとめて入力して処理したい」場面で、配列+ループは最強コンビになります。

この記事では、

  1. 配列の各要素に scanf で値を読み込む
  2. 読み込んだ値を表示する
  3. さらに一歩進めて、配列の並びを反転する(reverse)
  4. そのための 2値の交換(swap) の考え方

を、表と図でしっかり整理していきますね。

まず結論:配列への入力は scanf の第2引数がカギ

配列であっても、入力は「普通の変数」と同じ感覚です。
違いは、読み込み先が x[i] みたいに「要素」になること。

読み込み先scanf に渡すもの理由
普通の変数 x&xscanf は“格納先の場所(アドレス)”が必要
配列の要素 x[i]&x[i]要素も変数なので“場所”が必要

ここ、試験でも現場でもめちゃ大事です。
scanf("%d", x[i]); みたいに & を忘れると、だいたい事故ります。

サンプルプログラム

やること

  • 5個の「作業時間(分)」を入力して配列に入れる。
  • 入力した内容を一覧表示する。

プロジェクト名:chap5-4-1 ソースファイル名:chap5-4-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

// 配列の要素に作業時間(分)を読み込んで表示
#include <stdio.h>

int main(void)
{
    int work[5];

    printf("5回分の作業時間(分)を入力してください。\n");

    for (int i = 0; i < 5; i++) {          // 要素に値を読み込む
        printf("%d回目:", i + 1);
        scanf("%d", &work[i]);
    }

    puts("入力内容を確認します。");
    for (int i = 0; i < 5; i++)            // 要素の値を表示
        printf("work[%d] = %d\n", i, work[i]);

    return 0;
}

実行例

5回分の作業時間(分)を入力してください。
1回目:17
2回目:38
3回目:52
4回目:41
5回目:63
入力内容を確認します。
work[0] = 17
work[1] = 38
work[2] = 52
work[3] = 41
work[4] = 63

ここで登場した命令の書式と役割

for 文(配列を順番に処理する=走査)

書式

for (初期化; 条件式; 更新) {
    繰り返す処理
}

何をする命令?
指定した回数だけ繰り返して、配列の要素を先頭から順番に扱えます。

今回の読み込みループはこういう意味です:

部分意味
int i = 0添字を 0 から開始
i < 50~4 の範囲だけ繰り返す(5回)
i++1回ごとに添字を1増やす

printf(メッセージ表示)

書式

printf(書式文字列, 値1, 値2, ...);

何をする命令?
画面に文字や数値を整形して表示します。%d は整数の表示に使います。

scanf(キーボード入力を変数へ格納)

書式

scanf(書式文字列, 変数のアドレス);

何をする命令?
キーボードから読んだ値を、指定した場所(アドレス)へ書き込みます。

意味
scanf("%d", &work[i]);入力した整数を work[i] に格納する。

puts(1行表示して改行する)

書式

puts(文字列);

何をする命令?
文字列を表示して最後に改行します。
printf("...\n") の「文字列だけ版」みたいな感覚でOKです。

図で理解:&work[i] は「箱の住所」を渡している

scanf は「どこに保存する?」を知りたがります。
だから work[i] の“住所”を渡します。

  • work[i]:箱の中身
  • &work[i]:箱の場所(アドレス)

次の一歩:配列の並びを反転する(reverse)

入力して表示するだけだと、配列の「便利さ」がまだ半分くらいなんですよね。
そこでよく出る練習が 反転です。

反転とは、こういうこと

  • 先頭と末尾を交換
  • 2番目と後ろから2番目を交換
  • …を繰り返す

サンプルプログラム

やること

  • 7日分の「歩数」を入力
  • 配列の並びを反転
  • 結果を表示

プロジェクト名:chap5-4-2 ソースファイル名:chap5-4-2.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

// 7日分の歩数を読み込んで並びを反転して表示
#include <stdio.h>

int main(void)
{
    int steps[7];

    printf("7日分の歩数を入力してください。\n");

    for (int i = 0; i < 7; i++) {          // 要素に値を読み込む
        printf("%d日目:", i + 1);
        scanf("%d", &steps[i]);
    }

    for (int i = 0; i < 3; i++) {          // 要素の並びを反転(7なら交換は3回)
        int t = steps[i];
        steps[i] = steps[6 - i];
        steps[6 - i] = t;
    }

    puts("並びを反転しました。");
    for (int i = 0; i < 7; i++)
        printf("steps[%d] = %d\n", i, steps[i]);

    return 0;
}

反転の仕組み

要素数 7 の場合、交換ペアはこうなります。

i交換する要素ペア
0steps[0] と steps[6]先頭 ↔ 末尾
1steps[1] と steps[5]2番目 ↔ 後ろから2番目
2steps[2] と steps[4]3番目 ↔ 後ろから3番目

真ん中の steps[3] はそのままです(左右が同じ位置になるから)。

なぜ i < 3 でいいの?(半分だけ交換すればOK)

要素数 7 を反転するとき、全部7回交換したらどうなると思います?
後半でまた元に戻し始めちゃうんです。

だから 半分だけ交換します。

要素数 n交換回数の目安
52回
63回
73回
84回

「だいたい n/2 回」って覚えると気持ちいいです(整数なので切り捨て)。

2値の交換(swap)は必ず一時変数が必要

交換はこの3手順が基本です。

手順操作意味
t = a;a を退避
a = b;b を a に入れる。
b = t;退避しておいた a を b に入れる。

配列の反転では、

  • a に相当:steps[i]
  • b に相当:steps[6 - i]

になります。

これはダメ(なぜ?)

a = b;
b = a;

これだと、2行目の時点で a はもう b の値になっているので、
結局 両方とも同じ値になってしまいます。

ありがちなミス(ここで潰しておくと強い)

ミス結果
& を付け忘れるscanf("%d", steps[i]);ほぼ確実に不具合
ループ範囲がズレるi <= 7steps[7] を触って危険
交換相手の式が違うsteps[7 - i]範囲外アクセスになる(i=0でsteps[7])

要素数が 7 なら末尾添字は 6、だから 6 - i がちょうどいい、というわけです。