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

配列の使い方

初期値の代入
配列も、今までの変数と同様に、宣言と同時に初期化できます。
配列の初期化は、次のようにして行います。

配列の初期化
型名 配列名[要素数]={0番の数値,1番の数値,2番の数値,・・・};

{}で囲んだ中に順番に,で区切って数値を並べます。
すべてを指定する必要はないので、数値の個数は、配列の要素数以下にします。
配列の要素数よりも少ない場合、残りにはすべて0が代入されます。
次のプログラムは、配列を初期化して表示する例です。

ソースコード
#include <stdio.h>

int main(void)
{
    int array[10] = { 42, 79, 13 };

    printf("array[0] = %d\n", array[0]);
    printf("array[1] = %d\n", array[1]);
    printf("array[2] = %d\n", array[2]);
    printf("array[3] = %d\n", array[3]);
    printf("array[4] = %d\n", array[4]);

    return 0;
}

このプログラムの実行結果は、次の通りになります。

実行結果
array[0]= 42
array[1]= 79
array[2]= 13
array[3]= 0
array[4]= 0

この方法で宣言した場合、要素数を省略できます。
その場合は、指定した数値の個数だけの要素数が確保されます。
次のプログラムは、要素数を省略して配列を宣言する例です。

ソースコード
#include <stdio.h>

int main(void)
{
    int array[] = { 42, 79, 13 }; /* 要素数が省略されている */

    printf("array[0] = %d\n", array[0]);
    printf("array[1] = %d\n", array[1]);
    printf("array[2] = %d\n", array[2]);

    return 0;
}

要素数を指定する手間が省ける上に、数え間違いもなくなるので、
初期値を代入できる場合には省略する方が良いでしょう。

1度に配列に値を代入するのは、宣言の時にしかできません。
たとえば、次のような代入はできません。

できない代入
array = { 42, 79, 13 };
array[10] = { 42, 79, 13 };

これらの書き方は、コンパイルエラーとなってしまいます。
宣言のあとで値を代入したい場合は、面倒でも1つ1つ代入します。

できる代入
array[0] = 42;
array[1] = 79;
array[2] = 13;

全要素の表示
配列のすべての要素を表示するために、for文のループを使うことができます。
この様な使い方ができることこそ、配列の最大の利点です。
次のプログラムは、配列内の要素の数をすべて表示する例です。

ソースコード
#include <stdio.h>

int main(void)
{
    int array[] = { 42, 79, 13, 75, 19 };
    int i;

    for (i = 0; i < 5; i++) {
        printf("array[%d] = %d\n", i, array[i]);
    }

    return 0;
}

このプログラムの実行結果は次の通りになります。

実行結果
array[0] = 42
array[1] = 79
array[2] = 13
array[3] = 75
array[4] = 19

このプログラムでのポイントは、配列の要素番号には変数も使えることです。
変数の値を変化させれば、さまざまな番号の要素に直接アクセスできます。
配列ではない普通の変数では、5つの要素の表示にはprintf関数が5つ必要ですし、
100個表示するには100個必要ですが、配列ならfor文の数値を変えるだけです。

これなら、1万人のデータを表示させるプログラムだって簡単に書けます。
他にも、ループ内の文を変えることで、全要素に同じ値を代入したり、
全要素に代入された値の平均を求めることも簡単にできます。
要素数を求める
前項では、for文のループを使用して配列の要素をすべて表示しました。
繰り返し回数の5は直接指定しましたが、これでは要素数を数えなくてはなりません。
この方法では、要素数が増えるたびに、for文を書き直す必要があります。

それは面倒くさいので、要素数を自動的に求めて繰り返させることにします。
要素数を求める直接的な方法は用意されていませんが、計算することはできます。

配列全体のサイズを求め、それを要素1つのサイズで割れば要素の数がわかります。
C言語には、変数や配列のサイズを求めるsizeof(サイズオブ)演算子があります。
sizeof演算子は、次のようにして使います。

sizeof演算子
sizeof(変数や配列名)

sizeof演算子には()をつけなくても良いのですが、つけた方が読みやすいため、つけるのが一般的です。
この演算子を使って配列arrayの要素数を計算するには次のようにします。

配列arrayの要素数を求める
sizeof(array) / sizeof(array[0])

array[0]としているのは、配列の長さが1であっても、配列0番の要素は必ずあるからです。
array[1]としてしまうと、配列の長さが1のときには、エラーになってしまいます。

次のプログラムは、この方法で前項のプログラムを書き直した例です。

ソースコード
#include <stdio.h>

int main(void)
{
    int array[] = { 42, 79, 13, 75, 19 };
    int i;

    for (i = 0; i < sizeof(array) / sizeof(array[0]); i++) {
        printf("array[%d] = %d\n", i, array[i]);
    }

    return 0;
}

実行結果は、前回と同じになります。
配列の要素数を変えると、自動的にその数だけ表示します。

要素数を変数に格納してムダを省く
実は、上記のプログラムにはムダがあります。
なぜならループ1回毎に「sizeof(array) / sizeof(array<0>)」の計算をしているためです。

int array_count = sizeof(array) / sizeof(array<0>);
for (i = 0;i < array_count;i++) {

として、要素数を変数に格納しておくことで、毎回計算するムダを省くことができます。
まあ、今のパソコンはとても高性能なので、あまり大差ないんですけどね・・・

配列のコピー
ある配列のすべての要素の値を他の配列に代入するにはfor文を使います。

ソースコード
#include <stdio.h>

int main(void)
{
    int array1[] = { 42, 79, 13, 19, 41 };
    int array2[] = { 1, 2, 3, 4, 5 };
    int i;

    for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
        printf("array2[%d] = %d\n", i, array2[i]);
    }

    /* array1 の全要素を array2 にコピー */
    for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
        array2[i] = array1[i];
    }

    for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
        printf("array2[%d] = %d\n", i, array2[i]);
    }

    return 0;
}

このプログラムの実行結果は次の通りになります。

実行結果
array2[0] = 1
array2[1] = 2
array2[2] = 3
array2[3] = 4
array2[4] = 5
array2[0] = 42
array2[1] = 79
array2[2] = 13
array2[3] = 19
array2[4] = 41

結果を見ると、array1の値がarray2にコピーされてることがわかります。

しかし、for文を使わなくても、memcpy関数を使うことができます。
なお、memcpy関数を使うには、memory.h ファイルを #include する必要があります。

memcpy関数
memcpy(コピー先配列名、コピー元配列名、配列全体のサイズ)

配列全体のサイズは配列の型や要素数によって変わってくるので、
この関数では、sizeof演算子で得られたサイズを元にします。

配列のすべての要素をコピーするには、sizeof演算子で配列名を指定します。
次のプログラムは、memcpy関数で配列をコピーする例です。

ソースコード
#include <memory.h>
#include <stdio.h>

int main(void)
{
    int array1[] = { 42, 79, 13, 19, 41 };
    int array2[] = { 1, 2, 3, 4, 5 };
    int i;

    for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
        printf("array2[%d] = %d\n", i, array2[i]);
    }

    memcpy(array2, array1, sizeof(array1)); /* array1 の全要素を array2 にコピー */

    for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
        printf("array2[%d] = %d\n", i, array2[i]);
    }

    return 0;
}

実行結果は、先ほどとまったく同じになります。

バッファオーバーラン
上記では、コピー元の配列(array1)と、コピー先の配列(array2)は、同じ長さです。
したがって、はみ出したりすることはなく、ぴったりコピーされます。

しかし、困ったことに、memcpy関数は、それぞれの配列の長さを考慮してくれません。
コピー元の配列が、コピー先より長くても、無理やり全部コピーしてしまいます。

その結果として、長い部分がはみ出してコピーされてしまいます。
その結果、無関係の他の変数や配列を上書きして、重大な計算エラーを引き起こします。
これが、悪名高いバグである「バッファオーバーラン」です。
アプリやパソコンが突然止まったりする原因の多くが、このバッファオーバーランです。
まあ、厳密には、はみ出してコピーされただけの場合はバッファオーバーフローですが・・・

C言語は、とてもバグがおきやすい言語です。
ちょっとプログラムを間違えると、すぐにアプリが止まってしまいます。
JavaやC#やRustなどのより新しいプログラミング言語は、
バッファオーバーランがなるべくおこらないように設計されています。



本サイトについて

苦しんで覚える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

💬 コメント投稿欄を開く