C言語基礎|5章のまとめ

5章は、C言語で「同じ種類のデータをまとめて扱う」ための中心技術である配列を学びました。
配列が使えるようになると、同じ処理を何十回も書くのではなく、繰り返し(for)と組み合わせて短く安全に書けるようになります。

さらに、オブジェクト形式マクロで「数値に名前」を付けたり、多次元配列で「表」や「データのまとまり」を表現できたりして、プログラムの表現力が一気に上がります。
このまとめでは、5章の重要ポイントを一枚の地図みたいに整理していきますね。

配列とは何か(定義をやさしく整理)

配列は、同じ型の値(オブジェクト)を記憶域上に連続して一直線に並べたものです。

配列が「連続して並ぶ」イメージ

先頭 →末尾
a[0]a[1]a[2]a[3]a[4]

この「連続して並ぶ」が超重要で、添字(番号)でアクセスできたり、走査(順にたどる)が簡単になります。

配列を特徴づける3点

要素意味
要素型各要素の型int, double
要素数何個並ぶか5個、100個
名前配列を指す識別子scores, data

型の見方:Type の配列、Type[n] 型

ここは最初つまずきやすいので、表でまとめます。

宣言意味型の表現
int a[5];int が 5 個並ぶ配列int[5] 型
double x[7];double が 7 個並ぶ配列double[7] 型
int m[3][4];int[4] 型が 3 個並ぶ配列int[3][4] 型

ポイント:2次元配列では「要素型」が配列になります。
つまり、int m[3][4] の要素 m[0], m[1], m[2] は int[4] 型の配列です。

要素と添字:a[i] が意味すること

配列の要素へアクセスするのが、添字演算子 [ ] です。

添字演算子の意味(超重要)

表現意味
a[b]配列 a の先頭から b 個後ろの要素を読み書きする。

添字のルール(存在するのはここまで)

要素数が n の配列なら、存在する要素は

a[0], a[1], …, a[n-1]

です。
a[n] は存在しません。a[-1] も存在しません。

先頭と末尾の添字

要素数先頭の添字末尾の添字
n0n - 1

走査:配列を順にたどる(for と相性抜群)

配列の要素を先頭から順に処理するのが走査です。
走査は、ほぼ for で書きます。

for の書式と意味

書式役割
for (初期化; 継続条件; 更新) 文指定した回数や条件で繰り返す。

配列走査の定番形はこれです。

for (int i = 0; i < n; i++) {
    // a[i] を処理
}
  • i は添字
  • 0 から n-1 までを順に回す

初期化:{ } のルールを一気に整理

配列の初期化は、宣言と同時に値を入れる方法です。

初期化子の基本形

書式意味
Type a[n] = {値1, 値2, ...};先頭から順に初期化する。

よく使う規則まとめ

パターン結果
要素数を省略int b[] = {4, 8, 15};要素数は 3 になる
初期化子が足りないint c[5] = {1, 2};残りは 0 になる
{0}int d[5] = {0};全要素が 0 になる
最後のコンマ{11, 22, 33,}付けてもOK

注意:宣言だけの int a[5]; は 不定値です(勝手に0にはなりません)。

オブジェクト形式マクロ:#define で「数字に名前」

配列の要素数などの「決まり値」を、プログラム中に直接書くと管理が大変です。
そこで、オブジェクト形式マクロを使います。

#define の書式と意味

書式意味
#define a b以降の a を b に置換する。
  • a はマクロ名(慣習で大文字にすることが多い)
  • b は置換後の値

これで、マジックナンバー(意味が分かりにくい生の数値)を減らせます。

多次元配列:配列を要素に持つ配列

2次元配列は「表」のイメージです。

3行4列の2次元配列(int m[3][4])

  • 行:先頭側の添字
  • 列:末尾側の添字
列0列1列2列3
行0m[0][0]m[0][1]m[0][2]m[0][3]
行1m[1][0]m[1][1]m[1][2]m[1][3]
行2m[2][0]m[2][1]m[2][2]m[2][3]

並び順(末尾側の添字が優先して増える)

記憶域上の並びは、ざっくりこういう順です。

m[0][0], m[0][1], m[0][2], m[0][3], m[1][0], ...

この「列が先に進む」感覚は、2重ループの書き方にも直結します。

配列は代入で丸ごとコピーできない(超重要)

C言語では、配列同士を

b = a;

のように代入してコピーすることはできません。
コピーしたいなら、要素を1つずつ代入する必要があります。

代入式の評価:a = b = 0 が成立する理由

代入式は「代入した後の左辺の値」を返します。
だから、

b = 0; // b は 0 になる。式の値も 0
a = (b = 0); // a に 0 を代入できる

という流れになって、a と b の両方が 0 になります。

ただし、宣言の初期化で int a = b = 0; はできません。
初期化は式の評価ルールが同じノリで使えない、という認識でOKです。

5章まとめ用サンプルプログラム

在庫数(1次元配列)をコピーして合計し、2次元配列で「3日×2店舗の売上」を表示するプログラムを例に解説をします。

#include <stdio.h>

#define ITEMS 5      // 在庫データの個数
#define DAYS  3      // 日数
#define SHOPS 2      // 店舗数

int main(void)
{
    // 1次元配列:在庫数(コピー元)
    int stock_src[ITEMS] = {12, 5, 0};   // {12, 5, 0, 0, 0}
    int stock_dst[ITEMS];                // 不定値(これからコピーして埋める)

    // 1次元配列のコピー(要素ごと)
    for (int i = 0; i < ITEMS; i++)
        stock_dst[i] = stock_src[i];

    puts("在庫のコピー結果を表示します。");
    for (int i = 0; i < ITEMS; i++)
        printf("品目%d:元=%d 先=%d\n", i, stock_src[i], stock_dst[i]);

    // 合計(走査)
    int total = 0;
    for (int i = 0; i < ITEMS; i++)
        total += stock_dst[i];
    printf("コピー後の在庫合計は %d です。\n", total);

    // 2次元配列:3日×2店舗の売上(円)
    int sales[DAYS][SHOPS] = {
        {1200,  900},
        {1500, 1100},
        { 800, 1300},
    };

    puts("売上表(Day x Shop)を表示します。");
    for (int d = 0; d < DAYS; d++) {
        for (int s = 0; s < SHOPS; s++) {
            printf("%4d", sales[d][s]);
        }
        putchar('\n');
    }

    return 0;
}

このプログラムで復習できること

章の要点該当コード何を確認できる?
マクロで要素数管理#define ITEMS 5数字の一括管理、マジックナンバー減
初期化の不足分は0{12, 5, 0}残りが0になる規則
配列コピーは逐一代入stock_dst[i] = stock_src[i];b = a ができない理由
走査for (int i...)合計など集計の定番
2次元配列sales[d][s]行列っぽいデータの扱い
末尾添字が先に増える2重ループ列→行の順で並ぶ感覚