SSブログ

値渡し(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 チイ



nice!(0)  コメント(0) 
共通テーマ:学問

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。