C++のポインタ渡し:関数で呼び出し元の変数を変更する伝統的な方法

C++の関数は、return 文を使って呼び出し元に値を返すことができますが、返却できる値は原則として1つだけです。

もし、1つの関数で「複数の計算結果」を呼び出し元に返したい場合、どうすればよいでしょうか。例えば、一連のデータを分析して「最大値」と「最小値」の両方を取得したい場合などです。

この問題を解決する伝統的な(C言語由来の)方法が、ポインタ(アドレス)を関数に渡すテクニックです。

目次

ポインタ渡しとは?

「ポインタ渡し」とは、変数のそのものを渡す(値渡し)代わりに、変数が格納されているメモリアドレス& 演算子で取得)を関数に渡す方法です。

関数側(仮引数)は、そのアドレスを**ポインタ(*)**として受け取ります。

これにより、関数は受け取ったポインタを間接参照(* 演算子)することで、呼び出し元の関数のスコープにあるオリジナルの変数を直接読み書きできるようになります。

ポインタ渡しのサンプルコード

以下の例では、findMinMax という関数が、商品の価格リスト(double型配列)を分析し、その結果(minPricemaxPrice)を呼び出し元の main 関数が用意した変数に書き込みます。

#include <iostream>
#include <array>    // std::array を使用
#include <limits>   // 最小値・最大値の初期化に使用

/**
 * @brief 配列から最小値と最大値を見つけ、ポインタ経由で結果を書き込む
 * @param data (in) 分析対象のデータ
 * @param outMin (out) 最小値を格納する場所へのポインタ
 * @param outMax (out) 最大値を格納する場所へのポインタ
 */
void findMinMax(const std::array<double, 5>& data, double* outMin, double* outMax)
{
    // ポインタが nullptr でないかチェック (安全のため)
    if (outMin == nullptr || outMax == nullptr) {
        return; 
    }

    double currentMin = std::numeric_limits<double>::max();
    double currentMax = std::numeric_limits<double>::lowest();

    for (double price : data) {
        if (price < currentMin) {
            currentMin = price;
        }
        if (price > currentMax) {
            currentMax = price;
        }
    }

    // --- ここが重要 ---
    // ポインタが指す先のメモリアドレスに、見つけた値を書き込む
    *outMin = currentMin;
    *outMax = currentMax;
}

int main() {
    std::array<double, 5> productPrices = {19.99, 25.50, 15.00, 30.10, 18.75};

    // 1. 結果を格納するための「箱」(変数)を main 関数で用意する
    double minResult = 0.0;
    double maxResult = 0.0;

    // 2. findMinMax 関数に、変数の「アドレス(&)」を渡す
    findMinMax(productPrices, &minResult, &maxResult);

    // 3. 関数が main の変数を変更したことを確認する
    std::cout << "--- 商品価格分析結果 ---\n";
    std::cout << "最小価格: " << minResult << " ドル\n";
    std::cout << "最大価格: " << maxResult << " ドル\n";

    return 0;
}

実行結果:

--- 商品価格分析結果 ---
最小価格: 15.00 ドル
最大価格: 30.10 ドル

main 関数は minResultmaxResult のアドレス(&)を渡しました。findMinMax 関数は、それらを outMinoutMax というポインタとして受け取り、*outMin = ...*outMax = ... という操作で main 関数側の変数を直接変更しました。

現代C++における代替手段

この「ポインタ渡し」による出力方法はC++で有効なテクニックですが、いくつかの欠点があります。

  • 呼び出し側は & を付ける必要があり、面倒。
  • 関数側は * で間接参照する必要があり、読みにくい。
  • nullptr を渡される(あるいは渡してしまう)危険性がある。

現代のC++では、同じ目的を達成するために、より安全でクリーンな代替手段が推奨されます。

代替案1:参照渡し (&)

前章で解説した「参照渡し」は、ポインタ渡しの最も直接的な代替手段です。

// ポインタ (double*) の代わりに 参照 (double&) を使用
void findMinMax_Ref(const std::array<double, 5>& data, double& outMin, double& outMax)
{
    // ... (計算ロジックは同じ) ...
    
    // '*' や '->' が不要になり、通常の変数と同じように代入できる
    outMin = currentMin;
    outMax = currentMax;
}

int main() {
    double minResult = 0.0;
    double maxResult = 0.0;
    
    // 呼び出し側で '&' が不要になり、クリーンになる
    findMinMax_Ref(productPrices, minResult, maxResult);
    
    // ...
}

C++において、このような「出力用引数」には、nullptr を渡せない(安全性が高い)という理由から、ポインタ渡しよりも参照渡しが圧倒的に好まれます

代替案2:structstd::tuple を返す

最も現代的で推奨される方法は、出力用引数を一切使わず、複数の値をまとめた struct(構造体)や std::tuplereturn で返すことです。

#include <iostream>
#include <array>
#include <limits>
#include <tuple> // std::tuple や std::tie を使用

// 戻り値の型として、2つの double を持つ std::tuple を指定
std::tuple<double, double> findMinMax_Return(const std::array<double, 5>& data)
{
    // ... (計算ロジックは同じ) ...
    
    // 最小値と最大値をタプルとして返す
    return std::make_tuple(currentMin, currentMax);
}

int main() {
    std::array<double, 5> productPrices = {19.99, 25.50, 15.00, 30.10, 18.75};

    double minResult = 0.0;
    double maxResult = 0.0;
    
    // 戻り値を変数に分解して受け取る (C++11 の std::tie)
    std::tie(minResult, maxResult) = findMinMax_Return(productPrices);
    
    // (C++17以降では、以下のようにさらに簡潔に書けます)
    // auto [minResult, maxResult] = findMinMax_Return(productPrices);

    std::cout << "最小価格: " << minResult << " ドル\n";
    std::cout << "最大価格: " < < maxResult << " ドル\n";
    
    return 0;
}

この方法は、関数が「何を入力(引数)とし、何を(return)で出力するのか」が明確になるため、最も読みやすく安全なコードとされています。

まとめ(総括)

  • **ポインタ渡し(Type*)**は、関数の引数として変数のアドレス(&)を受け取り、間接参照(*)によって呼び出し元の変数を変更する伝統的なテクニックです。
  • これにより、関数から複数の値を「返す」ことが(擬似的に)可能になります。
  • 現代のC++では、同じ目的のために、より安全で可読性の高い**参照渡し(Type&)**が推奨されます。
  • さらに言えば、出力用引数を使わずに、structstd::tuple で複数の戻り値をカプセル化し、return で返す方法が最も好ましい設計とされています。
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次