引数と戻り値

関数間でデータをやり取りする方法。値渡しと参照渡し

概要

関数にデータを渡す方法と、 関数から結果を受け取る方法は、 C++の基本的かつ最も大切なスキルです。

この章では、値渡しと参照渡しの違いを理解し、 適切なデータ交換方法を選べるようになります。

値渡し(Value Passing)

値渡しはデフォルトの方法です。 関数に渡された値はコピーされ、 関数内での変更は元の変数に影響しません。

#include <iostream>

void increment(int x) {
    x = x + 1;  // x はコピーなので、元の値は変わらない
    std::cout << "関数内: " << x << std::endl;
}

int main() {
    int num = 10;
    increment(num);
    std::cout << "関数外: " << num << std::endl;
    return 0;
}

関数内: 11
関数外: 10

num10 のままです。 これは関数が num のコピーである x を変更したからです。

参照渡し(Reference Passing)

参照渡しでは、 元の変数への「参照」を渡すため、 関数内での変更が元の変数に反映されます。

記号は & です。

#include <iostream>

void increment(int & x) {  // & が参照を示す
    x = x + 1;
    std::cout << "関数内: " << x << std::endl;
}

int main() {
    int num = 10;
    increment(num);
    std::cout << "関数外: " << num << std::endl;
    return 0;
}

関数内: 11
関数外: 11

今度は num11 に変わりました。 関数が元の変数そのものを変更したからです。

値渡し vs 参照渡し

特性 値渡し 参照渡し
記号 int x int & x
元の値への影響 なし(コピー) あり(参照)
メモリ効率 悪い(コピー) 良い(参照のみ)
用途 入力値のみ使用 値を変更したい
リスク 低い 意図しない変更も可

複数の戻り値(参照渡しの活用)

通常、関数は 1つ戻り値しか返せません。 しかし参照渡しを使えば、複数の値を「戻す」ことができます。

例:整数を秒・分・時間に変換

#include <iostream>

void convertTime(int totalSeconds, int & hours, int & minutes, int & seconds) {
    hours = totalSeconds / 3600;
    minutes = (totalSeconds % 3600) / 60;
    seconds = totalSeconds % 60;
}

int main() {
    int h, m, s;
    convertTime(3661, h, m, s);  // 1時間1分1秒
    
    std::cout << h << "時間 " << m << "分 " << s << "秒" << std::endl;
    return 0;
}

1時間 1分 1秒

3つの参照を受け取ることで、 複数の結果を「返す」ことができます。

const参照(読取専用)

関数に値を渡すときに状況に応じて:

  • 入力のみ(変更しない) → const参照
  • 入力と出力(変更する) → 参照
  • 入力のみ(小さいデータ) → 値渡し

#include <iostream>
#include <string>

// const 参照:読取専用、効率的
void printName(const std::string & name) {
    std::cout << "名前: " << name << std::endl;
    // name = "太郎";  // エラー! const だから変更できない
}

// 参照:値を変更する
void addOne(int & num) {
    num = num + 1;
}

int main() {
    std::string myName = "花子";
    printName(myName);  // 安全で効率的
    
    int x = 5;
    addOne(x);
    std::cout << x << std::endl;  // 6
    return 0;
}

戻り値の活用

例1: 複数の計算結果

#include <iostream>

struct Point {
    int x, y;
};

Point movePoint(Point p, int dx, int dy) {
    Point result;
    result.x = p.x + dx;
    result.y = p.y + dy;
    return result;
}

int main() {
    Point p = {5, 10};
    Point p2 = movePoint(p, 3, 4);
    std::cout << p2.x << ", " << p2.y << std::endl;  // 8, 14
    return 0;
}

例2: 条件付き戻り値

#include <iostream>

int divide(int a, int b, bool & success) {
    if (b == 0) {
        success = false;
        return 0;
    }
    success = true;
    return a / b;
}

int main() {
    bool ok;
    int result = divide(10, 2, ok);
    
    if (ok) {
        std::cout << "結果: " << result << std::endl;
    } else {
        std::cout << "ゼロで除算できません" << std::endl;
    }
    return 0;
}

結果: 5

実践例

例:ソート関数

#include <iostream>

void swapValues(int & a, int & b) {
    int temp = a;
    a = b;
    b = temp;
}

void bubbleSort(int arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swapValues(arr[j], arr[j + 1]);
            }
        }
    }
}

int main() {
    int numbers[] = {5, 2, 8, 1, 9};
    int size = 5;
    
    bubbleSort(numbers, size);
    
    for (int i = 0; i < size; i++) {
        std::cout << numbers[i] << " ";
    }
    std::cout << std::endl;  // 1 2 5 8 9
    return 0;
}

1 2 5 8 9

ポイント

  • 値渡し:コピーなので元の値は変わらない
  • 参照渡し(&):元の値を直接変更できる
  • const参照:読取専用で効率的
  • 大きなデータは参照で渡す方が効率的
  • 参照は複数の「戻り値」を返すのに有効

よくある誤り

誤り1: 参照渡しを忘れてしまう

void increment(int x) {  // & がない!
    x++;
}

int main() {
    int num = 10;
    increment(num);
    std::cout << num << std::endl;  // 10 のまま
}

誤り2: const 参照に値を代入しようとする

void process(const int & x) {
    x = 10;  // エラー! const だから変更不可
}

誤り3: 配列への誤解

void fillArray(int arr[10]) {  // 配列は自動的に参照
    arr[0] = 100;  // 元の配列が変更される
}

やってみよう

練習1: 2つの変数の値をスワップ(交換)する関数。

練習2: 平均と標準偏差を計算し、参照で返す関数。

練習3: 文字列を受け取り、大文字に変換する関数(参照渡し使用)。

チャレンジ: ベクトルの大きさを計算する関数と、単位ベクトルを計算する関数。

まとめ

  • 値渡し参照渡しの違いを理解する
  • 参照渡しで複数の値を「返す」ことができる
  • const参照で読取専用かつ効率的なインターフェース
  • 適切な方法を選ぶことが、バグの少ないコードにつながる