C++: C++の関数

最終更新:2026-08-31

これまでのレッスンでは、すべてのコードがmain()の中にあった。

しかしプログラムが大きくなると、main()は長く保守しにくくなる。

関数がこの問題を解決——コードを小さく焦点を絞ったモジュールに分割し、それぞれが1つのタスクを担当。


1. 関数とは何か?

(1) 1.1 現実の例え

日常のシナリオ プログラミングでの相当
テレビのリモコンで「電源」ボタンを押す turnOnTV()関数を呼び出す
テレビが一連のステップを実行(電源オン、チャンネル調整、画面表示) 関数本体の中のコード
テレビが内部でどう動くか知る必要がない 関数の「カプセル化」プロパティ

関数は特定のタスクを実行する再利用可能なコードブロック。

(2) 1.2 なぜ関数が必要か?

関数なし(悪い例):

CPP
#include <iostream>

int main() {
    // 最初の長方形の面積を計算
    double width1 = 5.0, height1 = 3.0;
    double area1 = width1 * height1;
    std::cout << "面積 1: " << area1 << std::endl;
    
    // 2番目の長方形の面積を計算
    double width2 = 7.0, height2 = 2.5;
    double area2 = width2 * height2;
    std::cout << "面積 2: " << area2 << std::endl;
    
    // 3番目の長方形の面積を計算
    double width3 = 4.0, height3 = 6.0;
    double area3 = width3 * height3;
    std::cout << "面積 3: " << area3 << std::endl;
    
    return 0;
}

関数あり(良い例):

CPP
#include <iostream>

// 関数を一度だけ定義
double calculateArea(double width, double height) {
    return width * height;
}

int main() {
    std::cout << "面積 1: " << calculateArea(5.0, 3.0) << std::endl;
    std::cout << "面積 2: " << calculateArea(7.0, 2.5) << std::endl;
    std::cout << "面積 3: " << calculateArea(4.0, 6.0) << std::endl;
    
    return 0;
}

比較:

関数なし 関数あり
コードの重複(コピペ) コードの再利用(一度書く)
複数箇所でロジックを変更 1つの関数本体を変更
保守が困難 保守が容易


2. 関数の定義

(1) 2.1 基本構文

TEXT 📖 参照専用
戻り型 関数名(パラメータリスト) {
    // 関数本体
    return 戻り値; // 戻り型がvoidなら省略可
}

(2) 2.2 関数の構成要素

部分 意味
戻り型 関数が返す値の型 intdoublevoid(なし)
関数名 呼び出しに使う名前(変数命名規則に従う) calculateAreaprintHello
パラメータリスト 関数が受け取る入力(空でも可) (double w, double h)
関数本体 関数が呼ばれたときに実行されるコード { return w * h; }
return 呼び出し元に結果を返す return area;

▶ サンプル 1:単純な関数を定義(難易度 ⭐)

CPP
#include <iostream>

// 関数定義:2つの数の和を計算
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;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
3 + 5 = 8


3. 関数の呼び出し

(1) 3.1 呼び出し構文

TEXT 📖 参照専用
関数名(引数リスト);
用語 意味
関数定義 関数コードを書く int add(int a, int b) { ... }
関数呼び出し 関数を使う add(3, 5);
パラメータ 関数定義の変数 int a, int b
引数 呼び出し時に渡される実際の値 3, 5
💡 重要: 引数はパラメータにコピーされる(値渡し)。コピーを避ける方法は後で説明。



4. 戻り値

(1) 4.1 return文

returnは2つのことをする:

  1. 呼び出し元に結果を返す
  2. 関数を即座に終了(後のコードは実行されない)
CPP
#include <iostream>

int max(int a, int b) {
    if (a > b) {
        return a; // 関数終了、aを返す
    } else {
        return b; // 関数終了、bを返す
    }
    std::cout << "この行は実行されない" << std::endl; // デッドコード
}

int main() {
    std::cout << "最大値: " << max(10, 20) << std::endl;
    return 0;
}

(2) 4.2 戻り値のない関数(void)

関数が何も返す必要がない場合、voidを使う:

CPP
#include <iostream>

void printWelcome() { // voidは戻り値なしを意味
    std::cout << "========== ようこそ ==========" << std::endl;
    std::cout << "これはvoid関数です——戻り値は不要" << std::endl;
}

int main() {
    printWelcome(); // void関数を呼び出し、値を受け取る必要はない
    return 0;
}
💡 ヒント: void関数で早く抜けるにはreturn;(値なし)と書ける。

TEXT 📖 参照専用
void printIfPositive(int x) {
    if (x <= 0) {
        return; // 早く終了
    }
    std::cout << x << "は正です" << std::endl;
}


5. 関数プロトタイプ(前方宣言)

(1) 5.1 問題:関数は使用前に定義が必要

CPP
#include <iostream>

int main() {
    std::cout << add(3, 5) << std::endl; // ❌ エラー: コンパイラはまだaddを知らない
    return 0;
}

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

コンパイラエラー:

TEXT 📖 参照専用
error: 'add' was not declared in this scope

(2) 5.2 解決策:関数プロトタイプ

関数プロトタイプ(前方宣言とも呼ぶ)はコンパイラに「この関数が存在する——完全な定義は後で来る」と伝える。

CPP
#include <iostream>

int add(int a, int b); // 関数プロトタイプ(セミコロンで終わる!)

int main() {
    std::cout << add(3, 5) << std::endl; // ✅ コンパイラはaddが存在することを知っている
    return 0;
}

int add(int a, int b) { // 関数定義
    return a + b;
}
💡 ルール:

  • 関数プロトタイプはセミコロンで終わる
  • パラメータ名は省略可能(型だけ):int add(int, int);
  • しかしパラメータ名を残すと可読性が向上


6. 練習:関数付き電卓

▶ サンプル 2:関数付き電卓を実装(難易度 ⭐⭐)

CPP 📖 参照専用
#include <iostream>
#include <iomanip>

// 関数プロトタイプ
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double divide(double a, double b);

int main() {
    double num1, num2;
    char op;
    
    std::cout << "最初の数を入力: ";
    std::cin >> num1;
    
    std::cout << "演算子を入力 (+, -, *, /): ";
    std::cin >> op;
    
    std::cout << "2番目の数を入力: ";
    std::cin >> num2;
    
    std::cout << std::fixed << std::setprecision(2);
    
    switch (op) {
        case '+':
            std::cout << "結果: " << add(num1, num2) << std::endl;
            break;
        case '-':
            std::cout << "結果: " << subtract(num1, num2) << std::endl;
            break;
        case '*':
            std::cout << "結果: " << multiply(num1, num2) << std::endl;
            break;
        case '/':
            if (num2 != 0) {
                std::cout << "結果: " << divide(num1, num2) << std::endl;
            } else {
                std::cout << "エラー: ゼロ除算!" << std::endl;
            }
            break;
        default:
            std::cout << "エラー: サポートされていない演算子!" << std::endl;
            break;
    }
    
    return 0;
}

// 関数定義
double add(double a, double b) {
    return a + b;
}

double subtract(double a, double b) {
    return a - b;
}

double multiply(double a, double b) {
    return a * b;
}

double divide(double a, double b) {
    return a / b;
}
論理コード 49 行(40 行制限超過、参照専用)

7. よくある間違い

(1) 7.1 関数プロトタイプの忘れ

間違い:

TEXT 📖 参照専用
int main() {
    foo(); // ❌ コンパイラはfooが何かわからない
    return 0;
}

void foo() {
    std::cout << "Hello" << std::endl;
}

修正: 関数プロトタイプを追加:

CPP
void foo(); // ✅ 関数プロトタイプ

int main() {
    foo();
    return 0;
}

void foo() {
    std::cout << "Hello" << std::endl;
}

(2) 7.2 プロトタイプと定義の不一致

間違い:

TEXT 📖 参照専用
int foo(int a); // プロトタイプ:intを返す

int main() {
    foo(5);
    return 0;
}

double foo(int a) { // ❌ エラー:定義はdoubleを返す——不一致!
    return a + 0.5;
}

(3) 7.3 return文の忘れ

間違い:

CPP
int add(int a, int b) {
    int sum = a + b;
    // ❌ returnを書くのを忘れた
}

int main() {
    int result = add(3, 5);
    std::cout << result << std::endl; // ゴミ値を出力
    return 0;
}
💡 ヒント: コンパイラはreturn忘れを警告しないかもしれないが、プログラムの動作は未定義——ゴミ値を出力したりクラッシュする。


▶ サンプル 3:関数のオーバーロード(難易度 ⭐⭐)

CPP
#include <iostream>
#include <string>

// オーバーロードされた関数 - 同じ名前、異なるパラメータ
int add(int a, int b) {
    return a + b;
}

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

std::string add(const std::string& a, const std::string& b) {
    return a + b;
}

int main() {
    std::cout << "int add: " << add(3, 5) << std::endl;
    std::cout << "double add: " << add(3.14, 2.86) << std::endl;
    std::cout << "string add: " << add("Hello, ", "World!") << std::endl;
    
    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
int add: 8
double add: 6
string add: Hello, World!

❓ よくある質問

Q 関数プロトタイプでパラメータ名を省略できる?
A できるが、残すと可読性が向上。int add(int, int);よりint add(int a, int b);と書くと、コードが自己文書化される。
Q 関数は複数の値を返せる?
A C++の関数は戻り値が1つ。複数の値を返すには:①参照パラメータを使う ②構造体やクラスを返す ③std::tupleを使う(C++11+)。
Q void関数でreturnを書ける?
A できる!return;(値なし)でvoid関数を早く抜けられる。入力検証に便利——無効なデータをチェックして処理前にreturn。
Q なぜmainの前にプロトタイプがない関数がコンパイルできる?
A コンパイラが寛容か、誤って関数定義をmainより前に置いた可能性。ベストプラクティス:常にプロトタイプを先に置く。

📖 まとめ


📝 練習問題

  1. 初級(難易度 ⭐): 2つの整数の積を返すint multiply(int a, int b)関数を書く。main()から呼び出して結果を出力。

  2. 中級(難易度 ⭐⭐): 数が素数かチェックするbool isPrime(int n)関数を書く。main()でユーザーに数を尋ね、結果を表示。

  3. 上級(難易度 ⭐⭐⭐): 以下の関数を持つプログラムを書く:

    • int fibonacci(int n) —— n番目のフィボナッチ数を返す
    • void printFibonacci(int n) —— 最初のn個のフィボナッチ数を出力

    ユーザーにnを入力させ、数列とn番目の値を表示。



8. 🚀 次へ

関数を理解できたので、次は関数パラメータ(レッスン11)——関数に値を渡す方法と、値渡しと参照渡しの違いを学ぶ。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%