関数の定義
C/C++ 言語では、関数を呼び出す前に、その関数を定義する必要がある。関数を定義するとき、関数が受け取る引数とその変数型、そして関数の戻り値の変数型も合わせて定義する必要がある。例えば、整数 a
と整数 b
の和を計算して返す関数 add
を定義し、main
関数の中で add
関数を呼び出して実行するには次のようにする。この場合、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;
}
関数のオーバーロード
C/C++ では関数を定義するときに、関数の中で使われる引数の変数型も合わせて定義する必要がある。例えば、int add(int x, int y)
で定義された関数を呼び出して使うとき、x
と 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++ のテンプレートを使用すると便利である。
値渡し・ポインタ渡し・参照渡し
C/C++ 言語では、関数の引数に値を渡すとき、値渡しとポインタ渡しの 2 種類が存在する。値渡しの場合は、その値のコピーを関数に渡すため、関数の内部でその値を変更しても、関数を抜けた後にその影響が残らない。これに対して、ポインタ渡しの場合は、値のポインタ(メモリ上の住所)を関数に渡すため、関数の内部でそのメモリ上の値を変更すると、関数を抜けた後にはその影響が残ったままとなる。
C++ 言語では、値渡しとポインタ渡しの他に参照渡しも存在する。参照渡しの場合、ポインタ渡しと同様に、関数内部で値を変更すると、関数を抜けた後にもその影響が残る。
値渡し
#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;
}
ポインタ渡し
#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;
}
参照渡し
#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;
}
関数の引数と戻り値に配列を指定する方法
関数の引数と配列
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;
s = sum(arr, sizeof(arr) / sizeof(arr[0]));
printf("%d\n", s);
return 0;
}
関数を定義するとき、引数の定義は次のようにしても同じくポインタ渡しとなる。
int sum(int arr[], int arr_size) {
int sum = 0;
for (int i = 0; i < arr_size; i++) {
sum += arr[i];
}
return sum;
}
入力した配列に対して何らかの変更を行なって、その変更結果を返すような関数を作る場合は、配列のポインタを引数として受け取り、何も返さない関数を作成すれば良い。例えば、次の関数は入力された配列中の各値を 1000 倍している例を表している。
#include <stdio.h>
void times1000(int* arr, int arr_size) {
for (int i = 0; i < arr_size; i++) {
arr[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]);
}
// 1000
// 2000
// 3000
// 4000
// 5000
return 0;
}
関数の戻り値と配列
関数の戻り値に配列を指定することができない。そのため、配列を返したい場合は、その配列のポインタを返すことになる。関数の内部で定義したローカル変数を関数の外へ返すためにグローバル変数的な扱い方をしたいので、関数の内部では 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;
arr = calc(10, 20);
for (int i = 1; i < arr[0]; i++) {
printf("%d\n", arr[i]);
}
// 30
// -10
// 200
// 0
// 10
return 0;
}