C言語で関数に配列を渡す方法

361, 2021-12-06

目次

C言語で関数に配列を渡す

C言語では関数と配列を扱うことができます。
関数の引数には配列を渡すことが可能です

関数に配列を渡すことで高度なプログラミングも可能になります。
この記事ではその方法について具体的に↓を解説します。

  • 関数に配列を渡す一般的な方法

  • 引数の配列の注意点

  • 関数の引数にポインタを使う

  • 関数の引数の定義、どちらを使う?

  • 要素数を省略しない

関連記事

関数に配列を渡す一般的な方法

配列を引数に取る関数は、↓のように定義します。

// int型の配列を引数に取る関数func
void func(int ary[]) {
    ...
}

↑のように関数の引数に「int ary[]」と書きます。
こうするとこの関数はint型の配列を引数に取るようになります。

この関数に配列を渡してみましょう。
↓のように配列を定義して、関数funcに渡します。

    // 配列aryの定義
    int ary[] = {1, 2, 3};

    // funcに配列aryを渡す
    func(ary);

関数funcに渡せるのはint型の配列なので注意が必要です。
コード全文は↓になります。

#include <stdio.h>

// int型の配列を引数に取る関数func
void func(int ary[]) {
    printf("%d\n", ary[0]);  // 1
    printf("%d\n", ary[1]);  // 2
    printf("%d\n", ary[2]);  // 3
}

int main(void) {
    // 配列aryの定義
    int ary[] = {1, 2, 3};

    // funcに配列aryを渡す
    func(ary);

    return 0;
}

引数の配列の注意点

さきほどの関数funcでは↓のように引数を定義しました。

void func(int ary[]) {
    ...
}

↑のint ary[]は一見すると配列に見えます。
え? 配列じゃないの? と思った方、実はそうなんです。
関数の引数に限っては、↑のような配列の定義は配列ではありません。

実体はただのポインタです

その証拠に↓のコードを実行してみてください。

#include <stdio.h>

void func(int ary[]) {
    // 引数aryにsizeofを使う
    printf("func: sizeof ary = %ld\n", sizeof ary);
}

int main(void) {
    // 配列aryの定義
    int ary[] = {1, 2, 3};

    // 配列aryにsizeofを使う
    printf("main: sizeof ary = %ld\n", sizeof ary);

    // 配列aryをfuncに渡す
    func(ary);

    return 0;
}

sizeofは変数などのバイト数を得る演算子です。
配列にsizeofを使った場合は通常、配列全体のバイト数が求まります。

↑のコードを実行するとGCCなどのコンパイラでは↓のような警告が出力されます。

warning: ‘sizeof’ on array function parameter ‘ary’ will return size of ‘int *’ [-Wsizeof-array-argument]
    5 |     printf("func: sizeof ary = %ld\n", sizeof ary);

エラーメッセージを日本語にすると「関数の引数aryのsizeofはint *のサイズを返します」になります。
つまり関数の引数aryに関してはsizeofはint型のポインタのサイズを返すよ、ということですね。

実行してみると↓のような結果になります。

main: sizeof ary = 12
func: sizeof ary = 8

main()関数内のprintf()では12バイトと出力され、func()内のprintf()では8バイトと出力されました。
main()関数内で定義しているaryint型の配列で要素数は3つです。
筆者の環境ではintのサイズは4バイトなので、「3 * 4バイト」で配列aryの全体サイズは12バイトになります。

いっぽうfunc()関数の引数aryはポインタなので、ポインタ変数のサイズである8バイトが求まります。
このようにfunc()の引数aryは一見すると配列に見えますが、実体はポインタになっているので注意が必要です。

関数の引数にポインタを使う

さきほどの関数func()の引数aryただのポインタであることがわかりました。
配列のための関数の引数はポインタの表記でも定義することができます。
たとえば↓のようにです。

void func(int *ary) {
    ...
}

↑の「int *ary」はポインタの定義になっていますが、この引数に対しても配列は渡すことが可能です。

#include <stdio.h>

// int型のポインタを引数に取る関数func
void func(int *ary) {
    printf("%d\n", ary[0]);  // 1
    printf("%d\n", ary[1]);  // 2
    printf("%d\n", ary[2]);  // 3
}

int main(void) {
    // 配列aryの定義
    int ary[] = {1, 2, 3};

    // funcに配列aryを渡す
    func(ary);

    return 0;
}

関数の引数の定義、どちらを使う?

関数の引数は、ポインタによる定義と配列による定義どちらのほうがいいのでしょうか?

void func1(int ary[]) {
    ...
}

void func2(int *ary) {
    ...
}

角カッコをつけた配列による定義(int ary[])は一見すると引数に渡されるのが配列だとわかります。
ポインタによる定義(int *ary)では型からは配列だとわかりませんが、引数名で配列だと判断できます。
一般に配列の場合は角カッコの方を使う人が多いみたいです。
しかしその場合は先ほどのsizeofについて注意が必要です。
またmalloc()などで確保した動的配列の場合はポインタを使うのが一般的です。

要素数を省略しない

角カッコを使った配列を引数に定義する場合は、要素数を省略しないことも出来ます。

void func(int ary[4]) {
    ...
}

配列の要素数を明示したいときはこの定義方法が使えるかと思います。
しかし、このような定義方法をしてもコンパイラは配列の要素数をチェックしてくれません
ですので、要素数が異なる配列を関数に渡してもエラーなどにはなりません。

void func(int ary[4]) {
    ...
}

int main(void) {
    int ary[10];
    func(ary);  // エラーにならない
    return 0;
}

コンパイラが警告もエラーも出してくれないので、あくまで視覚的な効果だけです。
その辺は注意が必要です。

おわりに

今回はC言語の関数に配列を渡す方法を解説しました。
配列はC言語では非常によく使われる機能です。
関数と配列をうまく使って効率的なプログラミングをしましょう。

(^ _ ^)

配列と関数はマブダチ

(・ v ・)

sizeofは策士



この記事のアンケートを送信する