読者です 読者をやめる 読者になる 読者になる

お茶漬けびより

学んだことを整理する場所です。主に、C++, Unreal Engine 4 (UE4) を扱います。

Re:ゼロから始めるポインタ入門 #2

ポインタの基礎からちょっとずつ難しくしていこうかと思っていたのですが、 それだと書きづらいので、もう好きなように書いていくことにしました。すみません……。

はじめに

今回からは、配列とポインタを関数の引数にするときの様々な方法を見ていきます。

仮引数が配列の関数に、配列を渡す

引数として渡す変数は以下のような配列です。

int array[ARRAY_LENGTH]; /* ARRAY_LENGTH は、定数 3 */

まず、実引数として配列を渡す方法を見ていきます。 以下のような関数を使います。

void ArrayPrint(int array[ARRAY_LENGTH]);

これを使う場合、以下のように配列を渡します。

#include <stdio.h>
#define ARRAY_LENGTH (3)

void ArrayPrint(int array[ARRAY_LENGTH]);

int main() {
    printf("Re:ゼロから始めるポインタ入門 #2\n");

    int array[ARRAY_LENGTH];
    int i;
    for (i=0; i<ARRAY_LENGTH; i++) {
        array[i] = i * i;
    }
    ArrayPrint(array);
    return 0;
}

void ArrayPrint(int array[ARRAY_LENGTH])
{
    int i;
    for (i=0; i<ARRAY_LENGTH; i++) {
        printf("array[%d]: %d\n", i, array[i]);
    }
    return;
}

初期化した array を引数として、関数 ArrayPrint() に渡して、ArrayPrint 内で printf を使って、array の中を出力しています。 結果は、以下のようになります。

Re:ゼロから始めるポインタ入門 #2
array[0]: 0
array[1]: 1
array[2]: 4

関数の引数と同じ配列を渡しているので、とくに何も考えずに使うことが出来ると思います。

追記

指摘を頂きました。
仮引数が要素数固定の配列でも、要素数が異なる配列を 渡すことが出来る。といった指摘です。 では、試してみましょう。

main 関数内で定義している array[ARRAY_LENGTH]array[3] にして、ARRAY_LENGTH の値を 10 にします。これでコンパイルをすると仮引数と実引数の要素数が異なるのですが、普通に通ってしまいます。実行すると以下のようになりました。

Re:ゼロから始めるポインタ入門 #2 指摘
array[0]: 0
array[1]: 1
array[2]: 4
array[3]: 0
array[4]: 0
array[5]: 0
array[6]: 1873092421
array[7]: 32708
array[8]: 0
array[9]: 0

思ってたよりは値がおとなしいですが、array[6]array[7] は、デタラメな値が入っていますね。上記のように、関数の仮引数に配列の要素数を指定してもコンパイルエラーを起こすことは出来ないようです。これは、仮引数に配列を指定しても、ポインタを指定したのと同じだからです。ただ、関数を以下のようにすれば一応、同じ配列の要素数ではないとコンパイルエラーを起こすことが出来ます。

void ArrayPrint(int (*array)[ARRAY_LENGTH])

関数を呼び出す時は、ArrayPrint(&array) のようにします。これは、多次元配列なので、以下のように書くことも出来ます。

void ArrayPrint(int array[][ARRAY_LENGTH])
void ArrayPrint(int array[2][ARRAY_LENGTH])

3つ書き方を示しましたが、どれも同じ意味です。こうすると今度は、行の指定を無視できるので、 結局、要素数の異なる配列を渡すことが出来てしまいます。
結論としては、関数の仮引数に配列を指定するのを止めて、次に説明するポインタとその要素数を指定する方法か、仮引数に配列を明記しつつ、内部の処理で配列の要素数が仕様で決めたものと異なる場合は、さっさとシステムを殺すように作るというのが解決策かなと思います(どなたか詳しい方いれば、ご指摘お願いします)。

追記は以上です。ご指摘ありがとうございました。 追記内容のコードは、以下に置いてあります。 github.com

仮引数がポインタの関数に、配列を渡す

次は、関数の仮引数が、ポインタの場合です。以下のような関数に配列を渡します。

void PointsPrint(int* points, size_t point_num) {
    int i;
    for (i=0; i<point_num; i++) {
        printf("points(%p): %d\n", points+i, *(points+i));        
    }
}

point_num には、引数の長さを渡します。 記事が長くなってしまうので、全文は避けますが、先ほど全文を載せたソースコードに、上記の関数 PointsPrint() を使います。渡す実引数は、先ほどと同じ array[ARRAY_LENGTH] です。
渡し方は、以下のようになります。

PointsPrint(array, ARRAY_LENGTH);

実行すると、以下のようになります。

Re:ゼロから始めるポインタ入門 #2
points(0x7ffff1384160): 0
points(0x7ffff1384164): 1
points(0x7ffff1384168): 4

array と書くことで、配列の先頭( array[0] )を渡すことになります。 配列は、同じ型のポインタが並んでいるみたいなものなので、関数 PointsPrint() のように ポインタとして扱えます。
ただ、配列のサイズが分からなくなってしまうので、引数として配列のサイズを受け取る必要があります。 逆に言えば、ポインタとして扱えば、どんなサイズの配列でも扱うことが出来るようになります。

今回は、以上です。 コードの全文は、以下に置いてあります。

github.com