【C言語入門】配列の使い方まとめ(初期化、代入、宣言、コピー)

配列って使ってますか?

配列は同じ型のデータを大量に扱う場合に一つのかたまりとして扱うことができるので便利です。

この記事では、配列について

  • 配列の使い方について
  • ポインタでのアドレスアクセスについて
  • 浅いコピー、深いコピーについて
  • 文字列を配列で宣言・定義する方法
  • 配列の要素数を取得する方法
  • 関数の引数に配列を指定する方法
  • 多次元配列の使い方


など基本的な内容から、応用的な使い方の内容についても解説していきます。

今回は配列について、使い方をわかりやすく解説します!

目次

配列の使い方について

配列とは、同じ型のデータを並べたかたまりです。違う型のデータを混ぜることはできません。

配列の中のそれぞれのデータを配列の要素といいます。

宣言方法について

配列を宣言するには、配列名の前にデータの型を記述します。

配列名の後には「[ ]」で要素数を囲みます。

データ型名 配列名[要素数];

例えば、以下のようになります。

int int_arr[5];
char char_arr[5];

初期化の方法について

初期化の方法は宣言の際に「{ }」を使って要素を囲む方法と、for文を使って要素1個ずつに値を代入する方法があります。

宣言の際に「{ }」内に各要素を記述し初期化する場合は、「[ ]」内の要素数の記述を省略することもできます。

「{ }」を使って要素を囲む場合は各要素を「,」(カンマ)で区切ります。

char型の配列は文字列となります。

文字列を初期化する場合は「” “」で文字列を囲む方法もあります。

それではサンプルコードで確認していきましょう。

#include <stdio.h>
 
int main(void) {
    // 宣言と同時に初期化する方法
    int int_arr1[5] = {0, 1, 2, 3, 4};
 
    // 全ての要素をゼロで初期化する方法
    int int_arr2[5] = {};
    for(int i = 0; i < 5; i++) {
        if(i < 4) {
            printf("%d,", int_arr2[i]);
        } else {
            printf("%d\n", int_arr2[i]);
        }
    }
 
    // 途中まで初期化
    int int_arr3[5] = {0, 1, 2};
    for(int i = 0; i < 5; i++) {
        if(i < 4) {
            printf("%d,", int_arr3[i]);
        } else {
            printf("%d\n", int_arr3[i]);
        }
    }
 
    // ひとつの要素だけゼロ以外を指定
    int int_arr4[5] = {[2] = 2};
    for(int i = 0; i < 5; i++) {
        if(i < 4) {
            printf("%d,", int_arr4[i]);
        } else {
            printf("%d\n", int_arr4[i]);
        }
    }
 
    // 要素数の記述を省略
    char char_arr1[] = {'H', 'e', 'l', 'l', 'o'};
    printf("%s\n", char_arr1);
 
    // 文字列で初期化
    char char_arr2[16] = "Hello";
 
    return 0;
}

実行結果:

0,0,0,0,0
0,1,2,0,0
0,0,2,0,0
Hello

このサンプルコードでは、まずint型の配列を初期化する例を記述しています。

宣言の記述の後に「=」(イコール)を記述し、「{ }」で囲まれ「,」(カンマ)で区切られた要素を順に記述しています。ゼロで初期化する場合には「{ }」のように記述を省略することもできます。

途中まで初期値を指定した場合は、指定されなかった要素はゼロで初期化されます。「{ }」内で要素と初期値を指定し、それ以外は全てゼロで初期化することもできます。

char型の配列を初期化する例として、一文字ずつ初期化する方法と文字列で初期化する方法が記述されています。

代入の方法について

配列の要素に値を代入する方法は要素の番号を指定して1つずつ代入するか、もしくはfor文を使って代入するかのどちらかです。

サンプルコードで確認していきましょう。

#include <stdio.h>
 
int main(void) {
    int int_arr1[5] = {0, 1, 2, 3, 4};
    for(int i = 0; i < 5; i++) {
        int_arr1[i] = 4 - i; // for文を使って1個の要素ずつ代入
        if(i < 4) {
            printf("%d,", int_arr1[i]);
        } else {
            printf("%d\n", int_arr1[i]);
        }
    }
 
    int int_arr2[5] = {};
    int_arr2[2] = 2; // 要素に代入
    for(int i = 0; i < 5; i++) {
        if(i < 4) {
            printf("%d,", int_arr2[i]);
        } else {
            printf("%d\n", int_arr2[i]);
        }
    }
 
    // 文字列で初期化
    char char_arr2[16] = "Hello";
    char_arr2[0] = 'h'; // 要素に代入
    printf("%s\n", char_arr2);
 
    return 0;
}

実行結果:

4,3,2,1,0
0,0,2,0,0
hello

ポインタでのアドレスアクセスについて

C言語ではポインタにアスタリスクを付けるとポインタが示すアドレス内の値を取得することができます。

ポインタを使って要素の値を取得する方法について説明します。

まず配列と同じ型のポインタの宣言をします。宣言したポインタに配列の0番目のアドレスを指定します。

宣言したポインタに配列を代入すると0番目のアドレスを指定したことになります。アドレスを1つずつ移動しながらアドレス内の値を取得することで、要素の値を順に取得することができます。

それではサンプルコードで確認していきましょう。

#include <stdio.h>
 
int main(void) {
    int int_arr1[5] = {0, 1, 2, 3, 4};
    int *p1;
    p1 = int_arr1; // p1 = &int_arr1[0];
    for(int i = 0; i < 5; i++) {
        if(i < 4) {
            printf("%d,", *(p1+i));
        } else {
            printf("%d\n", *(p1+i));
        }
    }
 
    char char_arr2[16] = "Hello";
    char *p2;
    p2 = char_arr2; // p2 = &char_arr2[0];
    int num = sizeof(char_arr2) / sizeof(char_arr2[0]);
    for(int i = 0; i < num; i++) {
        if(i < num - 1) {
            printf("%c,", *(p2+i));
        } else {
            printf("%c\n", *(p2+i));
        }
    }
 
    return 0;
}

実行結果:

0,1,2,3,4
H,e,l,l,o,,,,,,,,,,,

このサンプルコードではint型のポインタ「p1」とchar型のポインタ「p2」を宣言しています。

「p1」ポインタは「int_arr1」配列のアドレスを「p2」ポインタは「char_arr2」配列のアドレスを指定するように記述しています。

ポインタにアスタリスクを付けて、それぞれのポインタが示すアドレス内の値が取得できるようにしています。実行結果を確認しても、ポインタを1つずつ移動することで各要素の値が取得できていることがわかります。

なお、char配列からポインタを使って要素の値を取得する際にsizeof演算子を使ってfor文の繰り返し回数を算出しています。これは文字で初期化されていない要素の値も取得するためです。

ちなみにsizeof演算子は型や変数のバイト数を出力するために使用します。sizeof演算子の使い方については、後ほど詳しく解説しています。

浅いコピー、深いコピーについて

ある配列を全ての要素を別の配列にコピーしたい場合があります。

配列をコピーする方法について説明します。

配列をコピーする方法は主に2つあります。for文を使って1個ずつ要素を代入する方法memcpy関数を使う方法です。これらは深いコピーと呼ばれるものになります。

コピーには浅いコピーと深いコピーの2種類があります。参照先が同じコピーが浅いコピー参照先が異なるコピーが深いコピーになります。

浅いコピーでは参照先が同じですのでコピー元の配列の要素の値が変わると、コピー先の配列の要素の値も変わります。深いコピーでは参照先が異なるのでコピー元の配列の要素の値が変わっても、コピー先の配列の要素の値が変わることはありません。

for文を使って1個ずつ要素を代入する方法とmemcpy関数を使う方法は深いコピーになります。

なお、memcpy関数を使うにはヘッダーファイル「string.h」をインクルードする必要があります。

それではサンプルコードで確認していきましょう。

#include <stdio.h>
#include <string.h>
 
int main(void) {
    int int_arr1[5] = {0, 1, 2, 3, 4};
    int int_arr2[5];
    int int_arr3[5];
 
    // 要素を1個ずつ代入コピー(深いコピー)
    for(int i = 0; i < 5; i++) {
        int_arr2[i] = int_arr1[i];
    }
 
    // memcpy関数を使用(深いコピー)
    memcpy(int_arr3, int_arr1, sizeof(int) * 5);
    for(int i = 0; i < 5; i++) {
        if(i < 4) {
            printf("%d,", int_arr3[i]);
        } else {
            printf("%d\n", int_arr3[i]);
        }
    }
 
    // 浅いコピー
    int *arr;
    arr = int_arr1;
    int_arr1[0] = 10;
    printf("%d\n", arr[0]);
 
    return 0;
}

実行結果:

0,1,2,3,4
10

このサンプルコードではfor文を使って1個ずつ要素を代入することで、「int_arr1」配列を「int_arr2」配列に深いコピーをしています。

また、memcpy関数を使用して「int_arr1」配列を「int_arr3」配列に深いコピーをしています。さらに「arr」ポインタに「int_arr1」配列を代入し浅いコピーを行っています。

この場合コピー元の「int_arr1」配列の0番目の要素を10に変えると「arr」の0番目の要素も10に変わることが実行結果からわかります。

文字列を配列で宣言・定義する方法

先ほど配列の初期化のところで、文字列の初期化についてもお伝えしました。

そこでは、配列での宣言・定義しましたが、他にもポインタ変数で宣言・定義する方法もあります。

文字列を宣言・定義する方法について、サンプルコードで確認しながらまとめてみましょう。

#include <stdio.h>
 
int main(void) {
    // 文字の配列で初期化
    char char_arr1[] = {'H', 'e', 'l', 'l', 'o'};
    printf("%s\n", char_arr1);
 
    // 文字列で初期化
    char char_arr2[16] = "Hello";
    printf("%s\n", char_arr2);
    
    // 文字列のポインタ変数で宣言
    char *str = "Hello";
    printf("%s\n", str);
    
    return 0;
}

実行結果:

Hello
Hello
Hello

このサンプルコードでは、文字列を配列とポインタ変数で宣言・定義する方法を全部で3種類挙げています。

文字の配列での宣言、ポインタ変数での宣言どちらもよく使われますので、使いこなせるようにしておきましょう。

配列の要素数を取得する方法

C言語では配列の要素数を取得する関数は用意されていません。

バイト数を取得するsizeof演算子を使って配列のサイズ・長さを取得してから、計算して要素数を取得します。

それでは、配列のサイズ・長さを取得する方法、要素数を取得する方法についてみていきましょう。

サイズ・長さを取得する方法

配列のサイズ・長さを取得するには、sizeof演算子を使います。

sizeof演算子の使い方をサンプルコードで確認しましょう。

#include <stdio.h>
 
int main(void) {
    int arr[3] = {0, 1, 2};
    
    int num = sizeof(arr);
    printf("arrのバイト数:%d\n", num);
    
    return 0;
}

実行結果:

arrのバイト数:12

このサンプルコードでは、sizeof演算子を使って配列のサイズ・長さをバイト数で取得しています。

要素数を取得する方法

先ほどは配列のサイズ・長さをバイト数で取得しました。

配列の要素1個のサイズ・長さもsizeof演算子で取得して、以下の計算をすれば要素数は取得できます。

配列の要素数 = (配列のサイズ・長さ) / (要素1個のサイズ・長さ)

サンプルコードで確認しましょう。

#include <stdio.h>
 
int main(void) {
    int arr[3] = {0, 1, 2};
    
    int num = sizeof(arr) / sizeof(arr[0]);
    printf("arrの要素数:%d\n", num);
    
    return 0;
}

実行結果:

arrの要素数:3

このサンプルコードでは、sizeof演算子を使って配列の要素数を計算しています。

計算結果の要素数と配列の宣言時の要素数が一致していることが確認できます。

sizeof演算子の使い方についてはこちらで詳しく解説しているので、ぜひ確認してください!

関数の引数に配列を指定する方法

関数の引数に配列を指定したい場合があります。

しかし、C言語では配列そのものを関数の引数に指定することはできません。配列のポインタであれば、関数の引数に指定することができます。

配列のポインタを関数の引数に指定する方法については、詳しくはこちらのサイトで解説しています。

ぜひ参照してくださいね。

多次元配列の使い方

これまでは1次元配列の使い方についてお伝えしてきました。

配列は必ずしも1次元とは限らず、2次元や3次元などの多次元配列を扱いたい場合もあります。

2次元や3次元などの多次元配列を扱う方法については、こちらで詳しく解説しています。

ポインタを使えば、2次元以上の多次元配列も1次元配列と同じように扱うことができます。その扱い方について詳しく解説していますので、ぜひこちらを参考にしてくださいね。

配列の使い方総まとめ

この記事では紹介しきれなかった配列のいろいろな使い方を次の記事にまとめているので、ぜひ確認してください!

まとめ

ここでは、配列の使い方について説明しました。

配列は型や要素数を宣言し、初期化して使います。また要素の値を代入して配列を操作します。C言語では要素の値をポインタを使って操作することもできます。

使いこなすことができるように、この記事を何度も参考にして下さいね!

この記事を書いた人

熊本在住のフリープログラマ兼ライターです。C/C++/C#、Java、Python、HTML/CSS、PHPを使ってプログラミングをしています。専門は画像処理で最近は機械学習、ディープラーニングにはまっています。幅広くやってきた経験を活かしてポイントをわかりやすくお伝えしようと思います。
お問合せはこちらでも受け付けています。
info@sss-lab.com

目次