1. はじめに
エンジニアとして3年が経過しようとしている中、技術を使ってより高度な問題解決をしていくためには、フレームワークや言語に依存しない基礎となる知識が必要だと感じたため、CSを勉強し直しています。
その学習内容を備忘録として発信します。
前回「変数」に関する記事を投稿しましたが、今回はその中でも変数についての「値渡し」と「参照渡し」と「参照の値渡し」という部分についてです。
2. 話の進め方
プログラミングにおいて「変数の扱い方」は非常に重要な概念です。
プログラムがデータを扱う際、言語ごとにどのような変数の扱い方をするかによってパフォーマンスなどに影響が出てきますので理解しておくことは重要です。
「値渡し」「参照渡し」「参照の値渡し」という順番で解説を進めます。
3. 値渡しとは
値渡し(Pass by Value)
値渡しとは、関数の引数に変数を渡すときに、「変数の値をコピーして渡す」方法です。
そのため、関数内で値を変更しても、元の変数には影響を与えません。
特徴
- 元の変数に影響を与えない(コピーを操作するため)。
- 主に基本データ型(プリミティブ型)で使用される。
- メモリの消費が増える(コピーを作成するため)。
- 安全だが、大きなデータ構造には非効率的。
def modify_value(x)
x = 10 # 新しい値を代入
puts("関数内の x:", x)
end
a = 5
modify_value(a)
puts("関数外の a:", a) # 元の変数 a は変わらない
関数内の x: 10
関数外の a: 5
→ modify_value 関数の x は、a のコピーを受け取るので、関数内で変更しても a には影響なし。
4. 参照渡しとは
参照渡し(Pass by Reference)
参照渡しとは、「変数が格納されているメモリ上のアドレスを渡す」方法です。
「使う変数はここにありますよ」という情報をそのまま渡すため、関数内で値を変更すると、元の変数にも影響を与えます。
特徴
- プリミティブ型、オブジェクト型(リスト、辞書、クラスのインスタンスなど)関係なく使用される。
- メモリ効率が良い(コピーを作らない)。
- 元の変数が変更される可能性がある。
- 意図しない変更が起こるリスクがある。
- C, Java, Ruby, Python などには参照渡し機能がない(C++, C#, PHP などにはある)。
#include <stdio.h>
void modify_value(int& x) {
x = 10; // 新しい値を代入
printf("関数内の x: %d\n", x);
}
int main(void) {
int a = 5;
modify_value(a);
printf("関数外の a: %d\n", a); // 元の変数 a が変わる
}
関数内の x: 10
関数外の a: 10
→ modify_value 関数の x は、a を参照しているので、関数内で変更すると a が変更される。
5. 参照の値渡しとは
参照の値渡しとは、オブジェクト指向言語でのオブジェクト型で用いられる渡し方であり、「作成されたオブジェクトへの参照(オブジェクトが格納されているメモリ上のアドレスなど)を変数に格納し、その参照を値渡しする(参照をコピーして渡す)」方法です。
特徴
- オブジェクト型(リスト、辞書、クラスのインスタンスなど)で使用される。
- メモリ効率が良い(オブジェクトのコピーを作らない)。
- 元のオブジェクトが変更される可能性がある。
- 意図しない変更が起こるリスクがある。
def modify_list(lst):
lst.append(4) # リストに値を追加
print("関数内の lst:", lst)
my_list = [1, 2, 3]
modify_list(my_list)
print("関数外の my_list:", my_list) # 元のリストが変更される
関数内の lst: [1, 2, 3, 4]
関数外の my_list: [1, 2, 3, 4]
→ リスト(変数の値)の参照が渡されるので、関数内でリストを変更すると元のリストが変更される。
前提として、「基本は値渡し」というものがあります。「参照の値渡し」の場合、「作成されたオブジェクトが格納されているメモリ上のアドレス情報を変数に格納し、その値を渡す」という性質上、「参照渡し」のように見えるという点が理解するのに厄介な点だと思います。しかし、変数には「オブジェクトの格納されているメモリ上のアドレスそのもの」が格納されているため、その値を渡すという以上、動作としては「値渡し」になります。
その上で、もし呼び出し先の関数内でオブジェクトの値の変更などが起こった場合、呼び出し元のオブジェクトの値も変更されるという事象が起こるわけです。
※後程図解を掲載予定
6. まとめ
しっかりと調べてみると、言語ごとに変数の扱い方が異なり、パフォーマンスへの影響もそれぞれ異なってくるということが分かりました。こうした言語仕様をしっかりと理解した上でプログラミングをするという一段上のレベルを目指していきたいです。