関数の基本

コードを整理して再利用する方法。プログラムの核となる概念

概要

プログラムが複雑になると、同じ処理を何度も書く必要が出てきます。 そこで活躍するのが関数です。

関数とは、一連の処理をまとめて、 名前を付けたもの。 与えられた入力(引数)に対して 処理を行い、結果(戻り値)を返します。

関数の基本形

戻り値の型 関数名(引数) {
    // 処理
    return 戻り値;
}

各要素:

  • 戻り値の型:関数が返す値のデータ型。戻り値がない場合は void
  • 関数名:その関数を呼び出す時に使う名前
  • 引数:関数に渡される値。複数可。不要な場合は空
  • return:関数を終了して値を返す

シンプルな例

例1: 戻り値ありの関数

#include <iostream>

int add(int a, int b) {
    return a + b;
}

int main() {
    int result = add(3, 5);
    std::cout << "3 + 5 = " << result << std::endl;
    return 0;
}

3 + 5 = 8

add関数は2つの整数を受け取り、その合計を返します。

例2: 戻り値なしの関数(void)

#include <iostream>

void greet(std::string name) {
    std::cout << "こんにちは, " << name << "!" << std::endl;
}

int main() {
    greet("太郎");
    greet("花子");
    return 0;
}

こんにちは, 太郎!
こんにちは, 花子!

void型の関数は値を返しません。 画面に出力したり、データを変更したりする処理に使われます。

関数の宣言と定義

通常、プログラムは上から順に実行されるため、 関数を定義する前に呼び出すことはできません。

これを解決する方法が関数の宣言です。

#include <iostream>

// 関数の宣言(プロトタイプ)
int multiply(int a, int b);

int main() {
    int result = multiply(4, 5);
    std::cout << result << std::endl;
    return 0;
}

// 関数の定義
int multiply(int a, int b) {
    return a * b;
}

20

;で終わる行を宣言(プロトタイプ)、 {}を含む部分を定義と呼びます。

複数の引数

例1: 3つの数値の最大値

#include <iostream>

int maxOfThree(int a, int b, int c) {
    int max = a;
    if (b > max) max = b;
    if (c > max) max = c;
    return max;
}

int main() {
    std::cout << maxOfThree(10, 20, 15) << std::endl;  // 20
    std::cout << maxOfThree(5, 5, 5) << std::endl;    // 5
    return 0;
}

20
5

例2: 四則演算

#include <iostream>

double calculate(double a, char op, double b) {
    if (op == '+') return a + b;
    if (op == '-') return a - b;
    if (op == '*') return a * b;
    if (op == '/') return a / b;
    return 0;
}

int main() {
    std::cout << calculate(10, '+', 5) << std::endl;  // 15
    std::cout << calculate(10, '*', 3) << std::endl;  // 30
    std::cout << calculate(10, '/', 4) << std::endl;  // 2.5
    return 0;
}

15
30
2.5

スコープ(変数の範囲)

関数内で宣言された変数は、 その関数のスコープ内でのみ有効です。 関数の外からはアクセスできません。

#include <iostream>

void testScope() {
    int x = 10;  // x はここで生成
    std::cout << x << std::endl;
}  // x はここで破棄される

int main() {
    testScope();  // 出力: 10
    
    // std::cout << x << std::endl;  // エラー! x は存在しない
    
    return 0;
}

このおかげで、異なる関数で同じ名前の変数を使ってもぶつかりません。

実践例

例1: 成績判定関数

#include <iostream>

char getGrade(int score) {
    if (score >= 90) return 'A';
    if (score >= 80) return 'B';
    if (score >= 70) return 'C';
    if (score >= 60) return 'D';
    return 'F';
}

int main() {
    int scores[] = {95, 75, 65, 55};
    
    for (int i = 0; i < 4; i++) {
        std::cout << "スコア: " << scores[i]
                  << " 判定: " << getGrade(scores[i]) << std::endl;
    }
    return 0;
}

スコア: 95 判定: A
スコア: 75 判定: C
スコア: 65 判定: D
スコア: 55 判定: F

例2: 素数判定関数

#include <iostream>

bool isPrime(int num) {
    if (num <= 1) return false;
    if (num == 2) return true;
    if (num % 2 == 0) return false;
    
    for (int i = 3; i * i <= num; i += 2) {
        if (num % i == 0) return false;
    }
    return true;
}

int main() {
    for (int i = 1; i <= 20; i++) {
        if (isPrime(i)) {
            std::cout << i << " ";
        }
    }
    std::cout << std::endl;
    return 0;
}

2 3 5 7 11 13 17 19

ポイント

  • 関数はコードの再利用と整理に必須
  • 関数は戻り値の型・名前・引数・処理で構成
  • void は戻り値がない場合に使用
  • 関数内の変数はそのスコープ内でのみ有効
  • 関数の宣言定義を分けることで順序の問題を解決

よくある誤り

誤り1: 戻り値の型がない

add(int a, int b) {  // 戻り値の型がない!
    return a + b;
}

誤り2: return を忘れる

int multiply(int a, int b) {
    int result = a * b;
    // return を忘れた!
}

誤り3: 関数の宣言より先に使う

int main() {
    int x = add(3, 5);  // add はまだ宣言されていない!
    return 0;
}

int add(int a, int b) {
    return a + b;
}

やってみよう

練習1: 2つの点 (x1, y1) と (x2, y2) の距離を計算する関数を作成。

練習2: 3つの点の座標を受け取り、三角形の面積を計算する関数。

練習3: N番目のフィボナッチ数を計算する関数。

チャレンジ: ユーザーから得点を5つ受け取り、平均をともに出力する関数。関数内で入力を処理。

まとめ

  • 関数で処理をまとめて再利用性を高める
  • 戻り値・引数・スコープの概念を理解することが重要
  • void で戻り値を返さない関数も作成できる
  • プログラムが複雑になるほど、関数の価値が増す