記事には広告を含む場合があります。

C言語入門講座

【C言語】配列を関数の引数として渡す!サイズは渡す?ポインタは使う?

おゆ

組み込み系プログラマとして5年働いていた元エンジニアです。得意言語はC言語とC++。本サイトでは学生および新人組み込み系プログラマに向けてプログラミング知識をわかりやすく解説しています。

配列を引数として渡す方法を解説します。

方法は複数あるのですが、ここでは「これを使っておけばOK」という方法を紹介します。

配列を引数として関数に渡す方法

配列で受け取る方法【通常はこれ】

配列で受け取る方法

関数の定義:
戻り値の型 関数名(配列の型 配列名[], int 配列のサイズを入れる変数名)
{
    処理
    return 戻り値;
}

仮引数(関数定義側の引数)の配列はサイズ(要素数)を指定せずに定義します。

配列のサイズは別途変数として渡す必要があります。

関数の呼び出し:
関数名(配列名, 配列のサイズ);

配列のサイズ取得方法

【C言語】配列の長さ(要素数)取得方法・配列のコピー方法まとめ

配列を使っているとfor文で列の長さ分だけループさせたい場合や、配列のコピーを作りたい場合があります。 それらの方法を解説します。 記事の内容 配列の長さ(要素数)を求める方法 配列をコピーする方法 ...

続きを見る

例1:

#include <stdio.h>
int average(int x[], int x_size)
{
    int sum=0;
    for(int i=0; i<x_size; i++)
    {
        sum+=x[i];
    }
    return sum/x_size;
}
void main()
{
    int array[3]={1, 2, 3};
    int array_size=sizeof(array)/sizeof(array[0]);
    printf("average=%d\n",average(array, array_size));
}

実行結果

average=2

仮引数はサイズ未定義の配列にします。

average関数では引数として渡された配列と配列のサイズを元に平均値を求め、戻り値としています。

関数内で配列のサイズが必要になる場合は必ず引数で渡します。

理由は下で解説します。

ポインタで受け取る方法【条件によっては使用】

ポインタで受け取る方法

関数の定義:
戻り値の型 関数名(配列の型 *変数名, int 配列のサイズを入れる変数名)
{
    処理
    return 戻り値;
}

関数の呼び出し(同上):
関数名(配列名, 配列のサイズ);

例1はポインタを使って以下のように書き換えられます。

例2:

#include <stdio.h>
int average(int *x, int x_size) //変更点
{
    int sum=0;
    for(int i=0; i<x_size; i++)
    {
        sum+=*(x+i); //変更点
    }
    return sum/x_size;
}
void main()
{
    int array[3]={1, 2, 3};
    int array_size=sizeof(array)/sizeof(array[0]);
    printf("average=%d\n",average(array, array_size));
}

実行結果

average=2

例1と異なるのは仮引数の定義とxの値を足す行の2箇所です。

仮引数はそのままポインタに置き換えれば配列を受け取れます。

なぜなら実は引数として配列を渡すと配列の先頭アドレスが仮引数に代入されるからです。

つまりポインタxには配列arrayの先頭アドレスが入ります。

よって*xの値はarray[0]に等しいです。

ではarray[1]の値はxを使ってどのように表されるのでしょうか?

答えは*(x+1)です。

xは整数型のポインタなのでx+1は整数1つ分のアドレスを進めた先のアドレスを示します。
配列とポインタ

これを利用して配列内の要素を1つ1つ足し、合計値sumを計算しています。

ポインタxが配列の先頭アドレスを受け取っているのなら、例1の配列x[]にも同じ値が入ってるってこと?
おゆ
そうなんです。例2の*xと例1のx[0]は書き方が違うだけで全く同じ値を示します。同様に*(x+1)とx[1]、*(x+2)とx[2]も同じ値です。

通常は配列で受ける方法の方がコードが見やすくておすすめです

しかし簡易なコンパイラを使う環境ではポインタで受け取る方がプログラムの実行速度が速くなる場合があるので、環境によってはこちらの方法で記述します

配列のサイズ(要素数)は渡すべき?

数字の配列では配列サイズを渡す

なんで配列のサイズも渡すの?関数内で求めちゃダメなの?
おゆ
上で解説したように配列を引数として渡すとポインタが渡されるためです。sizeofで仮引数のサイズを求めてもポインタのサイズが返ってくるだけです。

試しに仮引数の配列のサイズを測ってみます。

例3:

#include <stdio.h>
void output_size(int x[])
{
    int x_size=sizeof(x)/sizeof(x[0]);
    printf("x size=%d\n",x_size);
}
void main()
{
    int array[3]={1, 2, 3};
    output_size(array);
}

実行結果

x size=2

arrayのサイズは3のはずなのにxのサイズは2と出力されました。

このときワーニングとして「'sizeof' on array function parameter 'x' will return size of 'int *'」というメッセージが出ていました。

つまりsizeof(x)は整数型のポインタのサイズ(8バイト)になり、x_sizeの値は2になります。

このように関数内では配列のサイズを正しく取得できないので、引数として渡す必要があります。

文字列なら渡す必要はない

文字列であれば最後に入っているNULL文字を終端とすればループ処理などが書けます。

おゆ
文字列の配列の最後の要素にはNULL文字\0が入るルールになっていましたね。

よってサイズを引数として渡す必要はありません。

例4:

#include <stdio.h>
void output(char x[])
{
    printf("x=%s\n",x); //xを一度に出力
    int i=0;
    while(x[i]!='\0') //NULL文字に当たるまでループ
    {
        printf("%c\n",x[i]); //一文字ずつ出力
        i++;
    }
}
void main()
{
    char array[]="Hello"; //自動で末尾にNULL文字が入る
    output(array);
}

実行結果

x=Hello
H
e
l
l
o

配列の値は関数内で書き換え可能

引数として配列を渡すときはポインタ渡しになるので、関数内で実引数(関数呼び出し時に渡す変数)の値を変更できます。

下の例ではreset関数内で渡された配列の値をすべて0にリセットしています。

例5:

#include <stdio.h>
void reset(int x[], int x_size)
{
    for(int i=0; i<x_size; i++)
    {
        x[i]=0;
    }
}
void main()
{
    int array[3]={1, 2, 3};
    int array_size=sizeof(array)/sizeof(array[0]);
    reset(array, array_size);
    printf("x[0]=%d\n",array[0]);
    printf("x[1]=%d\n",array[1]);
    printf("x[2]=%d\n",array[2]);
}

実行結果

x[0]=0
x[1]=0
x[2]=0

C言語で行き詰まったら…

C言語・C++がわからない時に質問できるサイト・サービス5選

C言語を独学で勉強しているけど内容がイマイチわからない。もはや何がわからないのかもわからない。 C++のエラーが解決できない。ググってもわからない。わかる人に解説してほしい。 こんなお悩みにお答えしま ...

続きを見る

ブログランキング参加中。クリックしてもらえると励みになります。

ブログランキング・にほんブログ村へ

-C言語入門講座