値渡し(call by value)と参照渡し(call by reference) [コンピューター]
私はプログラミングの学習において、プログラミング用語を正確にきちんと理解するというよりは、どちらかというと感覚で覚えてしまうタイプなのですが、最近ある雑誌の関数の引数の渡し方についての記事を読んでいて、自分のこれまでの理解とは違う箇所がありました。関数の引数の受け渡しには、
・値渡し(call by value)
・参照渡し(call by reference)
の2つがあります。「値渡し」は実引数を仮引数のコピーするので、仮引数の値を変えても実引数の値は変わりません。「参照渡し」は、仮引数が実引数の別名(エイリアス)になっているものを言います。仮引数の値を変えると実引数の値も変わってしまいます。
以下のswap.cとswap.cppはどちらもmain関数の変数a,bの中身を入れ替える定番のスワップ関数ですが、私はこれまで両方とも参照渡し(call by reference)と理解していたのですが、正しくは、
swap.c アドレスの値渡し
swap.cpp 参照渡し
になるそうです。「アドレスの値渡し」は、「値渡し」の事ですが、通常の値ではなくてアドレスを渡しているのでそう呼ぶそうです。C++には「値渡し」と「参照渡し」の2つがありますが、Cには「値渡し」しかありません。因みにJavaもCと同じく「値渡し」だけです。プリミティブ型が通常の「値渡し」で、参照型が「参照値の値渡し」になります。(余談になりますが、Javaではswap関数は作れません!!)
ただ、swap.cとswap.cppのswap関数は動作的には全く同じことをやっているだけなので、コンパイルして32ビットのx86系のアセンブラに落としてコードを確認してみたところ、両者は全く同じでした。(下記の「swap関数のアセンブラ抜粋」参照。)main関数のスタックフレームにプッシュした変数a、bのアドレスにアクセスすることで、変数a、bの中身を入れ替えています。その際変数aの一時的な避難場所として、swap関数のスタックフレームの局所変数tmpを使っています。下記の「スタックの状態」を参考にしてアセンブラコードを読めば、やっていることがよく分かると思います。(Cの「アドレスの値渡し」もC++の「参照渡し」もアセンブラコードのレベルでは同じものになってしまうので、個人的には両方とも「参照渡し」と呼んでもいいような気もします^^;ただ単に、CではC++の&を使った参照の書き方が出来ないと言うだけの話なので。)
多くのプログラミング言語は「値渡し」だけです。CやJava、Python、Lispなどの関数型言語などがそうです。一方で「参照渡し」もサポートしている言語としては、C++やC#、PHP、Fortran、Pascal、Perlなどがあります。
[swap.c]
#include <stdio.h>
void swap(int *a, int *b) /* C言語ではアドレスを受け取る */
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main(void)
{
int a = 1;
int b = 2;
swap(&a, &b);
printf("a=%d b= %d\n", a, b);
return 0;
}
[swap.cpp]
#include <iostream>
using namespace std;
void swap(int &a, int &b) /* C++では参照渡しにできる */
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
int main(void)
{
int a = 1;
int b = 2;
swap(a, b);
cout << "a=" << a << " b=" << b << endl;
return 0;
}
[swap関数のアセンブラ抜粋]
push %ebp
movl %esp %ebp
subl $16, %esp
movl 8(%ebp), %eax
movl (%eax), %eax
movl %eax, -4(%ebp)
movl 12(%ebp), %eax
movl (%eax), %edx
movl 8(%ebp), %eax
movl %edx, (%eax)
movl 12(%ebp), %eax
movl -4(%ebp), %edx
movl %edx, (%eax)
leave
ret
[スタックの状態]
| |
------------------- ↑
| | <- %espの指すアドレス |
------------------- |
| |
-------------------
| | swap関数のスタックフレーム
-------------------
| 1(aの値) | <- -4(%ebp) 局所変数tmp
------------------- |
| %ebp | <- %ebpの指すアドレス |
------------------- ↓
| 戻りアドレス |
-------------------- ↑
| aのアドレス | <- 8(%ebp) 第1引数 |
--------------------
| bのアドレス | <- 12(%ebp) 第2引数 main関数のスタックフレーム
--------------------
| | |
↓
(高アドレス)
by チイ
コメント 0