LoginSignup
5
5

More than 5 years have passed since last update.

値渡しと参照渡しについて思いを馳せる

Last updated at Posted at 2016-09-24

連休中の暇つぶしで「値渡しと参照渡しの違いについて、シンプルにかつ首尾一貫して説明する方法はないか」を考えていました。そこで自分なりにこんな説明を考えてみました、という話です。

なお、文中で「参照」という言葉を使っていますが、ぼくはC言語しか知らない(のと、C++は最近勉強はじめた)ため、「参照」という言葉は他の言語ではまた別の意味を持つかもしれません。

値渡しについて

「Cには値渡ししか存在しない」は周知の事実ですが、例えば以下のコードにおいて、関数呼び出しの第一引数にm(==10)を指定した場合、関数f()には10というスカラー値を渡します。
また、第二引数にポインタ&nを渡した場合、式&nを評価した結果得られるポインタ(スカラー値)を関数f()に渡します。

C
#include <stdio.h>

void f(int m_, int *n_)
{
    m_++;
    (*n_)++;
}

int main(int argc, char **argv)
{
    int m=1;
    int n=1;

    f(m, &n);
    printf("m=%d n=%d\n", m, n);  /* m=1 n=2 */

    return 0;
}

上記コードでは、関数に渡されたポインタ(n_)を関数内部で間接参照して被参照オブジェクト(n)にアクセスしていますが、それは渡されたオブジェクトを関数内部でどのように使うかの「用途の話」であって、引数の渡し方とは関係ないです。整数値だろうがポインタ値だろうが、オブジェクトを渡していることには変わりは無いです。

別名を使う

関数にポインタを渡す意図として、データコピーのオーバーヘッドを抑止したい理由もありますが、変数スコープの制約上、呼び出し先の関数内からは呼び出し元で宣言された実引数のオブジェクトにアクセスできないので、その制約を回避したいためもあると思います(バイナリハックとかはナシで)。

そこでC++の参照型の登場ですが、呼び出し元の実引数のオブジェクト(n)に関数内でローカルスコープを持つ別名n_を紐付けることで、呼び出し先の関数内では「n_」というシンボル名を使ってオブジェクトnを(あたかもnを扱うかのごとく)ハンドリングすることができます。

C++
#include <iostream>

void f(int m_, int &n_)
{
    m_++;
    n_++;
}

int main(int argc, char **argv)
{
    int m=1;
    int n=1;

    f(m, n);
    std::cout << "m=" << m << " n=" << n << std::endl;
    // m=1 n=2

    return 0;
}

上記のような引数渡しについて、本来なら「参照渡し」と言うべきなのでしょうが、「参照」というとポインタの間接参照とごっちゃになってしまうため、「参照」という言葉の使用はなるべく避けたい(ポインタ説明のためにとっておきたい)のです。

そこで僕は個人的に「オブジェクトの別名束縛」と呼んでいます。対して、俗に言う「値渡し」は「オブジェクトの値渡し」と呼んでいます(ポインタの値渡しはこれの一種)。

ただ、上記nn_のように対応関係にある実引数と仮引数はそもそもスコープが異なるのだから同一シンボル名を付与できるとか、あるいは配列の部分配列、定数、配列型配列の配列要素など名前の無いオブジェクトに名前をつけることも可能なので、「別名」というのもまたビミョーに名が体を表してないのですが…難しいですね。

5
5
9

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5