ユーニックス総合研究所

  • home
  • archives
  • c-hairetsu-toha

C言語の配列とは?その美しさを解説します

  • 作成日: 2022-06-17
  • 更新日: 2023-12-25
  • カテゴリ: C言語

C言語の配列の美しさを解説する

C言語では配列というデータ構造を使うことができます。
配列を使うと複数のデータを1つの変数にまとめることが可能です。

このC言語の配列はとても美しいデータ構造になっています。
この記事では配列とこの配列の美しさについて解説していきたいと思います。

関連記事

目が覚めるC言語のdo-while文の使い方【ループ処理、初心者向け】
明快!C言語のcontinue文の使い方
君はまだC言語のdefineのすべてを知らない【マクロ、プリプロセス】
プログラミングのポインタをわかりやすく解説【C言語】
コードで見るC言語とC++の7つの違い

C言語の配列とは何なのか?

すでに述べたようにC言語の配列とは複数のデータを1つにまとめるデータ構造のことを言います。
たとえばint型の変数a, b, cがあったとします。

int a = 1;  
int b = 2;  
int c = 3;  

上記の変数はすべて関連しているとします。
つまりカテゴリがあってそのカテゴリに属している変数です。
たとえば猫や犬や牛は動物というカテゴリになります。

そうするとこれらの変数はカテゴリでまとめたくなるものです。
そういう場合に配列を使うと同じカテゴリの変数をまとめることが出来ます。
配列を作る場合は↓のようにします。

    int ary[3] = { 1, 2, 3 };  

複数の雑多な変数がまとめられて美しくなっているのがわかると思います。
配列の定義では↓のように行います。

型 配列名[要素数] = 初期化子リスト;  

配列内の各変数を「要素[/G」と言います。
そして配列を定義するときは配列にいくつ要素を持たせるか、その要素数を指定します。
↑のように宣言と定義を同時にやる場合は初期化子リストで初期化用の値を書きます。

また宣言だけして初期化を行わない場合は↓のように書くこともできます。

型 配列名[要素数];  

たとえばint型の配列aryを要素数3で宣言する場合は↓のようになります。

    int ary[3];  

これは変数が3つ作られているのと同じことです。
普通の変数との違いはメモリ上で連続した領域に定義される点です。
配列内の各要素がメモリ上で並んで配置されているので、それぞれの変数を美しく管理できます。

これに初期化子リストを加える場合は↓のようになります。

    int ary[3] = {1, 2, 3};  

初期化子リスト

初期化子リストとは配列の初期化に使うリストのことです。
これはブレース({})を使って書きます。
たとえば配列の各要素を先頭から1, 2, 3という値で初期化したい場合は↓のように書きます。

    int ary[3] = {1, 2, 3};  

配列の要素にはary[0]ary[1]のように添え字を付けてアクセスできます。
↑のように初期化した配列の各要素をprintf()で出力してみます。

#include <stdio.h>  

int main(void) {  
    int ary[3] = {1, 2, 3};  

    printf("%d\n", ary[0]);  // 1  
    printf("%d\n", ary[1]);  // 2  
    printf("%d\n", ary[2]);  // 3  

    return 0;      
}  

出力結果↓。

1  
2  
3  

↑の結果を見ると配列内の各要素が指定した値で初期化されているのがわかります。

配列の添え字のアクセスは0から始まるので注意してください。
添え字0が配列の1番目の要素、添え字1が配列の2番目の要素です。

要素数の省略

配列の要素数は初期化子リストを書く場合は省略することができます。
たとえば初期化しリストで要素を3つ分初期化したとします。
そうするとその配列の要素数は3になります。

#include <stdio.h>  

int main(void) {  
    int ary[] = {1, 2, 3};  

    printf("%d\n", ary[0]);  // 1  
    printf("%d\n", ary[1]);  // 2  
    printf("%d\n", ary[2]);  // 3  

    return 0;  
}  

配列の要素数を求める

配列の要素数を求める場合はsizeof演算子を使った方法が一般的です。

たとえば「sizeof 配列」では配列全体のバイト数が求まります。
int型の配列で要素が3つなら「intのサイズ × 3」になります。
intのサイズが4なら求まるバイト数は12になります。

そして「sizeof 配列[添え字]」では配列の要素のサイズが求まります。
int型の配列でintが4バイトなら求まるサイズは4になります。

つまり配列全体のサイズを要素1つのサイズで割ると全体の要素数が求まります。
int型(4バイト)の要素数が3の配列なら全体のサイズは12です。
これに要素1つのサイズの4バイトを割ると「12 / 4 = 3」になります。

#include <stdio.h>  

int main(void) {  
    int ary[3];  // int(4バイト)型の要素数3の配列  
    int len = sizeof ary / sizeof ary[0];  

    printf("%d\n", len);  // 3  

    return 0;  
}  

ただしこのsizeofを使った方法は配列のみに機能します。
ポインタには機能しません。
たとえば配列はポインタに入れることができますが、そのポインタからはsizeofで配列の長さを求めることはできません。

#include <stdio.h>  

int main(void) {  
    int ary[3];  
    int *p = ary;  
    int len = sizeof p / sizeof p[0];  

    printf("%d\n", len);  // 2  

    return 0;  
}  

↑の結果を見るとlenが2になっているのがわかります。
これはsizeof pでポインタ変数のサイズが求まってしまっているのが原因です。
配列のサイズが求まっていないのです。
ですからそれを要素1つのバイト数で割るとこのような期待しない値になります。

この辺は間違えやすいので注意してください。

C言語の配列の美しい点

それでこのC言語の配列はどの点が美しいのでしょうか?
美しい仕様をいくつか紹介したいと思います。

まず1つ目がメモリ領域が連続している点です。
それから2つ目が初期化子リストによる0クリアができる点です。

  • メモリ領域が連続してて美しい
  • 0クリアが簡単で美しい

これらを順に解説します。

メモリ領域が連続してて美しい

C言語の配列内の各要素はメモリ領域が連続しています。
これのおかげでC言語では配列を使ったコードを柔軟に書くことができます。

たとえば配列の要素をfor文で参照する場合です。
普通は配列の長さに応じてカウント変数を定義します。
しかし配列に「番兵」を入れておけばカウント変数もいらなくなります。

たとえばint型の配列で番兵を-1にしたとしましょう。
そうするとこの配列をfor文で回す場合は↓のように書くことができます。

#include <stdio.h>  

int main(void) {  
    int ary[] = {1, 2, 3, -1};  

    for (int *p = ary; *p != -1; p += 1) {  
        printf("%d\n", *p);  
    }  

    return 0;  
}  

出力結果↓。

1  
2  
3  

pはポインタ変数ですがこれに配列のアドレスを入れています。
配列のアドレスを得たい場合は&はいりません。

ポインタ変数は演算で加算することができます。
1加算すると型のバイト数だけアドレス値が増えます。
ですので隣り合う配列内の要素を参照することができます。

これを利用してfor文と番兵を使って配列を回しています。
番兵がいなかったり配列の範囲外にアクセスするとプログラムがクラッシュすることがあるので注意してください。

0クリアが簡単で美しい

配列の各要素を0クリアしたい場合は初期化子リストで{0}を指定します。

#include <stdio.h>  

int main(void) {  
    int ary[3] = {0};  

    printf("%d\n", ary[0]);  // 0  
    printf("%d\n", ary[1]);  // 0  
    printf("%d\n", ary[2]);  // 0  

    return 0;  
}  

↑のように= {0};と書くだけで配列内のすべての要素を0で初期化できます。
すごい簡単で美しいですよね。
これは大変便利なので良く使われます。

またmemsetで配列を0クリアすることもできます。

#include <stdio.h>  
#include <string.h>  

int main(void) {  
    int ary[3];  

    memset(ary, 0, sizeof ary);  

    printf("%d\n", ary[0]);  // 0  
    printf("%d\n", ary[1]);  // 0  
    printf("%d\n", ary[2]);  // 0  

    return 0;  
}  

memset()は第1引数に対象のポインタ、そして第2引数に対象を埋める値を指定します。
第3引数には対象のバイト数です。
つまりmemset(ary, 0, sizeof ary);とやると配列全体を0で初期化することができます。

これは配列のメモリ領域が連続しているからできる芸当です。
美しい仕様によってコードが簡単になるという実例だと思います。

おわりに

今回はC言語の配列とその美しさについて解説しました。
配列をマスターすればC言語マスターに近づけますね。

🦝 < 配列は美しい

🐭 < 美しい仕様は正義