18
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

値渡し、ポインタ渡し、オブジェクト共有渡し、参照渡しの言語別対応表と用語説明

18
Last updated at Posted at 2025-05-26

image.png

関数実引数変数を指定したとき、仮引数にどんな渡し方ができるかの言語別対応表と、関連用語の説明を書いてみました。
値渡し参照渡しの理解の助けになりましたら幸いです。

タイトルにある「オブジェクト共有渡し」(call by object sharing) は、Wikipediaにある「参照の値渡し」で記載しています。

言語別対応表

\ 渡し方
  \
言語 \
値渡し
(値コピー)
 
ポインタ渡し
(ポインタ値コピー
値共有)
参照の値渡し
(参照値コピー
オブジェクト共有)
参照渡し
(コピーなし
変数共有)
C# できる
(int arg)
できる
(int *arg)
できる
(Object arg)
できる
(ref int arg)
Swift できる
(arg: Int)
できる できる できる
(arg: inout Int)
PHP できる
(int $arg)
できない できる
(object $arg)
できる
(int &$arg)
VB.NET できる
(ByVal arg
As
Integer)
できない できる
(ByVal arg
As Object)
できる
(ByRef arg
As Integer)
Rust できる
(arg: i32)
できる
(arg: *i32)
できない できる1
(arg: &i32)
C++ できる
(int arg)
できる
(int *arg)
できない2 できる3
(int &arg)
C できる
(int arg)
できる
(int *arg)
できない2 できない
Go できる
(arg int)
できる
(arg *int)
できる
(arg []int)
できない
Java
JavaScript
TypeScript
できる
(int arg)
できない できる
(Object arg)
できない
Python できない できない できる
(arg: int)
できない
Ruby できない できない できる
(arg: Integer)
できない
Dart できない できない できる
(int arg)
できない

一番左の「値渡し」の欄は値型変数の値渡しのことです。参照型変数の値渡しは「参照の値渡し」の欄に分けて記載しています。

言語名のリンクをクリックすると「参照渡しはない」というドキュメントが表示されます。

「できる」の下の括弧は仮引数の書き方の一例です

変数の種類

大きくは「値型変数」と「参照型変数」の2種類に分類されます。

値型変数

  • 変数は値そのものを保持する
  • 代入した値と変数が保持する値は同じ
値型変数(代入した値を保持)

ポインタ型変数(値型変数の一種)

  • ポイントされる値(pointee)はメモリ上のどこかにあり、変数(pointer)は値へのポインタ値(例えばメモリアドレス値)を保持して値をポイントする
  • 変数にはポインタ値を代入するので、代入した値と変数が保持する値は同じ(値型変数)
  • 言語によってはポインタをインクリメントしたり配列アクセスして別の値をポイントすることもできる(危険)
ポインタ型変数(代入したポインタ値を保持) --ポイントする(point)-->  値(pointee)
参考: 人差し指(pointer finger) --指す(point)-->  人(pointee)

参照型変数(オブジェクト型変数)

  • 参照されるオブジェクトはメモリ上のどこかにあり、変数は代入したオブジェクトへの参照値を保持してオブジェクトを参照する
    • どんな参照値かは言語側が勝手に決める
  • 代入した値と変数が保持する値は違う
参照型変数(参照値を保持) --参照する(reference)-->  オブジェクト(代入した値)

Pythonではすべての変数が参照型変数で、参照値としてオブジェクトID(オブジェクト識別値)を保持します。
CPython(C言語で実装されたPythonインタプリタ)では、オブジェクトIDとしてオブジェクトのメモリアドレス値を使います。

C++では、参照渡しした仮引数(実引数変数を参照する変数)のことを「参照型」と定義しているようですが、この記事ではオブジェクト(代入値)を参照する変数を参照型変数と定義しています。

参照型関数の引数 | Microsoft Learn

データ型による変数の分類

どんなデータ型が値型変数か参照型変数かは言語によって異なります。

言語\データ型 数値 配列 ポインタ 構造体 オブジェクト
C, C++ 値型 どちらでもない 値型 値型 なし
C# 値型 参照型 値型 値型 参照型
PHP 値型 値型 なし なし 参照型
Java, JavaScript 値型 参照型 なし なし 参照型
Python, Ruby 参照型 参照型 なし なし 参照型

関数の引数の種類

実引数(値か変数)

  • 関数を呼び出す側が引き渡す値(数値、ポインタ値、オブジェクトなど)、もしくは変数。
function(123);    // 値を渡す
int value = 123;
function(value);  // 変数を渡す

変数を渡す場合に、変数の値を渡す「値渡し」と、変数そのものを渡す(呼び出し元変数を参照する)「参照渡し」があります。(後述)

仮引数(変数)

  • 呼び出された関数が引き受ける変数
  • 関数内で仮引数に代入することもできる

引数への変数の渡し方の種類

大きくは「値渡し」と「参照渡し」の2種類に分類されます。
実引数に「変数」を指定した場合の仮引数への渡し方の種類です。
実引数に値を指定した場合は値渡し相当で、参照渡しはコンパイルエラーや実行時エラーになります。

値渡し (call by value, pass by value)

  • 変数が保持してる値を仮引数に渡す
    • 値型変数は 値のコピー を渡す
      • 関数内で値を変更しても呼出し元に影響しない
    • 参照型変数は 参照値のコピー を渡して 値を共有 する(参照の値渡し)
      • 関数内で値を変更すると呼出し元に影響する
  • 実引数と仮引数は独立変数
    • 仮引数に代入しても実引数に影響しない

ポインタ渡し(値渡しの一種)

  • ポインタ型変数が保持しているポインタ値(例えばメモリアドレス値)のコピーを仮引数に渡す
  • 実引数と仮引数は独立変数
    • 仮引数に代入しても実引数に影響しない
  • ポイント先の値は共有
    • ポイント先の値を変更すると呼び出し元に影響する

参照の値渡し(値渡しの一種、call by object, call by object-sharing)

  • 「オブジェクト渡し」や「オブジェクト共有渡し」ともいわれる
  • 参照型変数が保持している参照値のコピーを仮引数に渡す
  • 実引数と仮引数は独立変数
    • 仮引数に代入しても実引数に影響しない
  • 参照先のオブジェクトは共有
    • 参照先のオブジェクト内容を変更すると呼び出し元に影響する

参照渡し (call by reference, pass by reference)

  • 実引数に指定した変数そのものを仮引数に渡す
    • 仮引数から実引数を参照する渡し方
  • 実引数と仮引数は同一変数(別名、エイリアス)、変数共有
    • 仮引数に代入すると実引数(呼出し元変数)に代入される

「参照を渡す」を英語にすると pass a reference です。
「参照渡し」は pass by reference で、例えば go by bicycle (自転車で行く)の by で行く手段として自転車を使うことを表現しているように、変数を渡す手段として参照を使うことを表現していると見ることができます。
「手を渡す」と「手渡し」(手を使って何かを渡す、手は渡さない)のような違いです。

参考: 引数 - Wiipedia

参照渡し(さんしょうわたし、call by reference)はその実装手段の一つ(と見ることもできる)。変数に対する参照(アドレス情報)を渡す方法である(これは言語側が勝手に行う。C言語のように明示的にアドレス演算子を使うものは参照渡しとは呼ばない)。

私の不確かな記憶では、ポインタ型変数も参照型変数もなかった時代のFORTRANでは関数の実引数に変数を指定するとすべて参照渡し(変数共有)していたので、言語毎の関数に対して「call by value/reference」という用語が使われ、現代の言語では引数毎に値渡しか参照渡しかを指定できるので、引数に対して「pass by value/reference」という用語が使われるようになったのではないかと憶測してます。
あくまで私の個人的な憶測です。

動作確認プログラム

値型変数の値渡し、ポインタ渡し、参照渡し

C++
#include <stdio.h>

void increment(int w, int *x, int *y, int &z) { // wは値渡し、x,yはポインタ渡し、zは参照渡し
    w++;     // 仮引数(コピーされた値)をインクリメント
    x++;     // 仮引数(コピーされたメモリアドレス値)をインクリメント
    (*y)++;  // ポイント先の共有値をインクリメント
    z++;     // 仮引数(共有変数の値)をインクリメント
}

int main() {
    int a = 1;  // 値型変数a
    int b = 1;  // 値型変数b
    int c = 1;  // 値型変数c
    int d = 1;  // 値型変数d
    increment(a, &b, &c, d);  // a,dは変数指定、&b,&cは値(メモリアドレス値)指定
    printf("%d %d %d %d\n", a, b, c, d);  // 1 1 2 2
}

ポインタ型変数の値渡し、参照渡し

C++
#include <stdio.h>

void increment(int *x, int *&y) {  // xは値渡し、yは参照渡し
    (*x)++;  // 共有されたint型変数の値をインクリメント
    x++;     // コピーされたポインタ型変数をインクリメント
    (*y)++;  // 共有されたint型変数の値をインクリメント
    y++;     // 共有されたポインタ型変数をインクリメント
}

int main(void) {
    int values[] = {1, 22, 333};
    int *p0 = &values[0];  // ポインタ型変数p0
    int *p1 = &values[1];  // ポインタ型変数p1
    increment(p0, p1);
    printf("%d %d\n", values[0], *p0);  // 2 2
    printf("%d %d\n", values[1], *p1);  // 23 333
}
C++
#include <iostream>

class Data {
public:
    int value;

    Data(int value) {
        this->value = value;
    }
};

void call_by_pointer(Data *arg) {    // ポインタ型変数の値渡し・ポインタ渡し(独立変数)
    arg->value = 2;                  // ポイント先の値を変更すれば呼出し元に影響する
    arg = new Data(3);               // 仮引数に代入しても呼出し元に影響しない
}

void call_by_reference(Data *&arg) { // ポインタ型変数の参照渡し(共有変数)
    arg->value = 4;                  // ポイント先の値を変更すれば呼出し元に影響する
    arg = new Data(5);               // 仮引数に代入しても呼出し元に影響する
}

int main(void) {
    Data *data = new Data(1);
    std::cout << data->value << std::endl;  // 1
    call_by_pointer(data);
    std::cout << data->value << std::endl;  // 2
    call_by_reference(data);
    std::cout << data->value << std::endl;  // 5
}

参照型変数の値渡し、参照渡し

PHP
<?php

function increment(object $x, object $y, object &$z) {  // x,yは参照の値渡し、zは参照渡し
    $x->value = $x->value + 1;                // 共有オブジェクト内部に代入
    $y = (object)['value' => $y->value + 1];  // 仮引数(独立変数)に代入
    $z = (object)['value' => $z->value + 1];  // 仮引数(共有変数)に代入
}

$a = (object)['value' => 1];  // 参照型変数a
$b = (object)['value' => 1];  // 参照型変数b
$c = (object)['value' => 1];  // 参照型変数c
increment($a, $b, $c);
echo "{$a->value} {$b->value} {$c->value}\n";  // 2 1 2

情報処理試験問題例

情報処理試験では、call by value を「値呼出し」、call by reference を「参照呼出し」と表記しています。

情報処理試験問題の実行結果確認プログラム

C++
#include <stdio.h>

void add(int X, int& Y) {  // 値型変数、仮引数Xは値呼出し、仮引数Yは参照呼出し
    X = X + Y;
    Y = X + Y;
}

int main(){
    int X, Y;  // 値型変数

    X = 2;
    Y = 2;
    add(X, Y);
    
    printf("%d %d\n", X, Y);
}
実行結果
2 6

まとめ

値やオブジェクトへの参照を渡すことを「参照渡し」と説明しているブログや記事が沢山ありますが、情報処理用語的には正しくありません。
「値型は値渡し」「参照型は参照渡し」ではありません。
「参照を渡す」と「参照渡し」は別物です。

  • 値(オブジェクト)への「参照を渡す」(pass a reference) と「値を共有」します
  • 関数引数に変数を「参照渡し」(pass by reference) すると「変数を共有」します

情報処理用語の「値渡し」「参照渡し」は、関数の引数に変数を渡すときに使われる用語で、変数2種類と渡し方2種類を組み合わせて以下の4種類があります。

  1. 値型変数の値渡し(値コピー)
  2. 値型変数の参照渡し(変数共有)
  3. 参照型変数の値渡し(値共有、オブジェクト共有)
  4. 参照型変数の参照渡し(変数共有)

みなさんが使用している言語ではどれができるか正しく理解・説明できますでしょうか。
用語の本来の使い方を理解して、話し相手と話がかみ合わない時の参考にしていただけたら幸いです。

  1. Rustは「参照渡し」ではなく「借用」という用語を使用しています。仮引数に直接代入することはできずデリファレンスして代入するので参照渡しとは違う印象ですが、ポインタ型のようにnullにはならないなど参照渡しの特徴を持っています。

  2. C/C++の配列型変数は、ポインタ渡しや参照の値渡しのようなことができますが、配列サイズを指定して領域確保した配列型変数は後述の参照型変数(オブジェクト変数)のように参照値を保持していないので、参照の値渡しはできないと記載しました。 2

  3. C++の参照型変数やPHPのリファレンス変数は、変数を参照して変数を共有(エイリアス)します。本記事で説明している参照型変数とは定義が異なります。

18
9
4

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
18
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?