2.12.8. 関数#

2.12.8.1. 関数の定義#

C/C++ では、関数を定義するときに引数とその型、さらに戻り値の型を指定する必要があります。例えば、整数 a と整数 b の和を計算して返す関数 add を定義し、main 関数から呼び出す例は次の通りです。main 関数から add を呼び出すため、add 関数の定義を main の前に置いています。

#include <stdio.h>

int add(int x, int y) {
    int s = x + y;
    return s;
}

int main() {
    int a = 10;
    int b = 20;

    int s = add(a, b);
    printf("%d", s);

    return 0;
}

add 関数を main 関数の後に記述する場合は、関数プロトタイプを main の前に宣言する必要があります。

#include <stdio.h>

int add(int, int);

int main() {
    int a = 10;
    int b = 20;

    int s = add(a, b);
    printf("%d", s);

    return 0;
}

int add(int x, int y) {
    int s = x + y;
    return s;
}

2.12.8.2. 関数のオーバーロード#

C/C++ では、関数は引数の型まで含めて識別されます。例えば int add(int x, int y) に小数を渡すと、小数は整数に変換されてから計算されます。異なる型に対応した関数を同じ名前で定義できる機能を関数のオーバーロードと呼びます。

#include <stdio.h>

float add(int, int);
float add(float, float);
float add(int, float);
float add(float, int);

int main() {
    float a = 1.1;
    int b = 2;

    float s = add(a, b);
    printf("%f", s);

    return 0;
}

float add(int x, int y) {
    float s = (float)x + y;
    return s;
}

float add(float x, float y) {
    float s = x + y;
    return s;
}

float add(int x, float y) {
    float s = x + y;
    return s;
}

float add(float x, int y) {
    float s = x + y;
    return s;
}

このように同じ機能の関数を複数定義する場合、C++ のテンプレート機能を使うとより便利です。

2.12.8.3. 値渡し・ポインタ渡し・参照渡し#

関数に引数を渡す方法には次の種類があります。

  • 値渡し:引数のコピーが渡されるため、関数内で値を変更しても呼び出し元には影響がありません。

  • ポインタ渡し:変数のアドレスが渡されるため、関数内で値を変更すると呼び出し元に反映されます。

  • 参照渡し:C++ で追加された機能で、関数内で変更すると呼び出し元にも反映されます。

2.12.8.3.1. 値渡し#

#include <stdio.h>

void f(int x) {
    x += 1;
}

int main(void) {
    int x = 10;
    printf("%d\n", x); // 10
    f(x);
    printf("%d\n", x); // 10
    return 0;
}

2.12.8.3.2. ポインタ渡し#

#include <stdio.h>

void g(int* x) {
    *x += 1;
}

int main(void) {
    int x = 10;
    printf("%d\n", x); // 10
    g(&x);
    printf("%d\n", x); // 11
    return 0;
}

2.12.8.3.3. 参照渡し#

#include <stdio.h>

void h(int& x) {
    x += 1;
}

int main(void) {
    int x = 10;
    printf("%d\n", x); // 10
    h(x);
    printf("%d\n", x); // 11
    return 0;
}

2.12.8.4. 関数の引数と戻り値に配列を指定する方法#

2.12.8.4.1. 配列を引数に渡す#

C/C++ では関数に配列そのものを渡すことはできません。その代わり、配列の先頭要素のアドレス、つまりポインタを渡します。配列名は先頭要素へのポインタとして扱われます。

#include <stdio.h>

int sum(int* arr, int arr_size) {
    int sum = 0;
    for (int i = 0; i < arr_size; i++) {
        sum += arr[i];
    }
    return sum;
}

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};
    int s = sum(arr, sizeof(arr)/sizeof(arr[0]));
    printf("%d\n", s);
    return 0;
}

関数の引数として配列を指定する場合、次のように書くこともできます。

int sum(int arr[], int arr_size) { ... }

配列の内容を変更して反映させたい場合は、戻り値を持たず、ポインタ経由で配列を操作します。

#include <stdio.h>

void times1000(int* arr, int arr_size) {
    for (int i = 0; i < arr_size; i++) {
        arr[i] *= 1000;
    }
}

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};
    times1000(arr, sizeof(arr)/sizeof(arr[0]));

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

2.12.8.4.2. 配列を戻り値として返す#

関数の戻り値として配列を直接返すことはできません。その代わり、配列のポインタを返します。関数内部で static 修飾子を使うと、関数終了後もメモリが有効なままになります。

#include <stdio.h>

int* calc(int x, int y) {
    static int arr[6];
    arr[0] = sizeof(arr)/sizeof(arr[0]);
    arr[1] = x + y;
    arr[2] = x - y;
    arr[3] = x * y;
    arr[4] = x / y;
    arr[5] = x % y;
    return arr;
}

int main(void) {
    int* arr = calc(10, 20);

    for (int i = 1; i < arr[0]; i++) {
        printf("%d\n", arr[i]);
    }
    return 0;
}