関数パラメータの参照渡しとはどういうことなのか？

##関数パラメータの参照渡しとはどういうことなのか？

関数呼び出しで、関数(メソッド)のパラメータの渡し方には「値渡し」と「参照渡し」というのがあります。

ただ、Java や JavaScript には言語仕様としての参照渡しはなく、常に値渡しになります。

参照渡しの例ですが、最初に C# を例にして説明します。次のサンプルは２つのパラメータの値を交換するのに参照渡しを使っています。

```C#
using System;

public class SwapFunc
{
  static void Main(String[] args)
  {
    int x = 0;
    int y = 1;

    // x と y の値を交換
    Swap(ref x, ref y);

    Console.WriteLine("x = {0:d}, y = {1:d}\n", x, y);
  }

  // x と y の値を交換するメソッド
  static void Swap(ref int x, ref int y)
  {
    var u = x;
    x = y;
    y = u;
  }
}
```

これの IL (中間コード) は次のようになります。

コメントに動作を書きましたが、正確かどうかは別として、スタックマシン (仮想マシン) を使って、スタックフレーム上のデータをプッシュしたりポップしたりして計算を行っています。

このまま実行すると遅いので、通常、さらにネイティブコードに変換されて実行されます。

参照渡しに関わる部分は Main では、IL_0005 と IL_0006 のあたりです。ここで、スタックフレーム上のデータ x, y のアドレスをスタックに積んでいます。

関数 Swap 側で参照渡しに関わる部分は、ldind 命令を使っているあたりです。この命令は指定したオペランドの内容をアドレスとみなして、そのアドレスの行った先のデータをスタックに積んでいます。

```CIL
//  Microsoft (R) .NET Framework IL Disassembler.  Version 4.0.30319.18020
//  Copyright (c) Microsoft Corporation. All rights reserved.



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly Swap
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                             63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
  .hash algorithm 0x00008004
  .ver 0:0:0:0
}
.module Swap.exe
// MVID: {DDFC4E86-9AF5-4AC3-927E-267DAB2AEE1E}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x04700000


// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit SwapFunc
       extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // コード サイズ       39 (0x27)
    .maxstack  3
    .locals init (int32 V_0,
             int32 V_1)
    IL_0000:  nop
    IL_0001:  ldc.i4.0　　' 0 をプッシュ。
    IL_0002:  stloc.0　　' 0 をポップしスタックフレーム V_0 に保存
    IL_0003:  ldc.i4.1　　' 1 をプッシュ。
    IL_0004:  stloc.1　　' 1 をポップしてスタックフレーム V_1 に保存
    IL_0005:  ldloca.s   ' V_0　　V_0 のアドレスをプッシュ。
    IL_0007:  ldloca.s   ' V_1　　V_1 のアドレスをプッシュ。
    IL_0009:  call       void SwapFunc::Swap(int32&,　　' Swap をコール
                                             int32&)
    IL_000e:  nop　　　何もしない。
    IL_000f:  ldstr      "x = {0:d}, y = {1:d}\n"　　' フォーマットのアドレスをプッシュ
    IL_0014:  ldloc.0　　' V_0 の値をプッシュ。
    IL_0015:  box        [mscorlib]System.Int32　' スタックのトップをボクシング
    IL_001a:  ldloc.1　　' V_1 の値をプッシュ。
    IL_001b:  box        [mscorlib]System.Int32　' スタックのトップをボクシング
    IL_0020:  call       void [mscorlib]System.Console::WriteLine(string,　' Console.WriteLine をコール
                                                                  object,
                                                                  object)
    IL_0025:  nop  ' 何もしない。
    IL_0026:  ret　　' もどる。
  } // end of method SwapFunc::Main

  .method private hidebysig static void  Swap(int32& x,
                                              int32& y) cil managed
  {
    // コード サイズ       12 (0xc)
    .maxstack  2
    .locals init (int32 V_0)
    IL_0000:  nop
    IL_0001:  ldarg.0　　' 引数0をプッシュ。
    IL_0002:  ldind.i4　 ' スタックトップをアドレスとしてその行った先の内容をプッシュ。
    IL_0003:  stloc.0　　' ローカル変数 0 に格納。
    IL_0004:  ldarg.0 　' 引数0をプッシュ。
    IL_0005:  ldarg.1　 ' 引数1をプッシュ。
    IL_0006:  ldind.i4  ' スタックトップをアドレスとして、その行った先の内容をプッシュ。
    IL_0007:  stind.i4  ' スタックトップをアドレスとして、その行った先へストア。
    IL_0008:  ldarg.1　' 引数1をプッシュ。
    IL_0009:  ldloc.0　' ローカル変数0をプッシュ。
    IL_000a:  stind.i4　' その行った先の内容をプッシュ。
    IL_000b:  ret
  } // end of method SwapFunc::Swap

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // コード サイズ       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method SwapFunc::.ctor

} // end of class SwapFunc


// =============================================================

// *********** 逆アセンブルが完了しました ***********************
// 警告: Win32 リソース ファイル C:\workspace\dotNET\IL\Swap\Swap.res を作成しました。
```

次に、C 言語の例を見てみます。

C 言語の場合、VM (仮想マシン) は使用しない、つまり、いきなりネイティブコードを生成します。

ここでは、PC で使われている x86 と x64 について述べます。RISC や IBM などでは、基本的考え方は同じはずですが、Calling Convention (呼び出し規約) がそれぞれ異なるのでいろいろ異なります。

まず、C のソースですが、下のようなものとします。

```C
#include <stdio.h>

void swap(int*, int*);

int main(int argc, char* argv[]) {
  int x = 0;
  int y = 1;

  swap(&x, &y);

  printf("x = %d, y = %d\n", x, y);

  return 0;
}

void swap(int* x, int* y) {
  int u = *x;
  *x = *y;
  *y = u;
}
```

これのコンパイル結果は、次のようになります。(x86 の場合)

```ASM
	.file	"Swap.c"
	.section	.rodata
.LC0:
	.string	"x = %d, y = %d\n"
	.text
.globl main
	.type	main, @function
main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ecx
	subl	$36, %esp
	movl	$0, -8(%ebp) # 0 をスタックフレームのローカルアドレスに保存(x)
	movl	$1, -12(%ebp) # 1 をスタックフレームのローカルアドレスに保存(y)
	leal	-12(%ebp), %eax # y のアドレスを EAX にロード
	movl	%eax, 4(%esp) # EAX をスタックに積む。
	leal	-8(%ebp), %eax # x のアドレスを EAX にロード
	movl	%eax, (%esp) # EAX をスタックに積む。
	call	swap # 関数 swap を呼び出す。
	movl	-12(%ebp), %eax
	movl	-8(%ebp), %edx
	movl	%eax, 8(%esp)
	movl	%edx, 4(%esp)
	movl	$.LC0, (%esp)
	call	printf
	movl	$0, %eax
	addl	$36, %esp
	popl	%ecx
	popl	%ebp
	leal	-4(%ecx), %esp
	ret
	.size	main, .-main
.globl swap
	.type	swap, @function
swap:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$16, %esp
	movl	8(%ebp), %eax # 後から積んだパラメータ(x)を EAX にロード
	movl	(%eax), %eax # EAX の行った先のデータを EAX にロード
	movl	%eax, -4(%ebp) # EAX をローカル変数に保存。
	movl	12(%ebp), %eax # 最初に積んだパラメータ(y)を EAX にロード
	movl	(%eax), %edx # EAX の行った先のデータを EDX にロード
	movl	8(%ebp), %eax # 後から積んだパラメータ(x)を EAX にロード
	movl	%edx, (%eax) # EDX を EAX の行った先に保存
	movl	12(%ebp), %edx # 最初に積んだパラメータ(y)を EDX にロード
	movl	-4(%ebp), %eax # ローカル変数の内容を EAX にロード
	movl	%eax, (%edx) # EAX を EDX の行った先に保存。
	leave
	ret
	.size	swap, .-swap
	.ident	"GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
	.section	.note.GNU-stack,"",@progbits
```

x64 の場合は、関数の呼び出し規約が異なっており、整数や浮動小数点数は最適化しなくてもレジスタ渡しになります。

これは、x64 では、汎用レジスタが8本から16本に増えたためで、余ったレジスタを関数のパラメータとして活用しています。

```ASM
	.file	"Swap.c"
	.section	.rodata
.LC0:
	.string	"x = %d, y = %d\n"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$32, %rsp
	movl	%edi, -20(%rbp)
	movq	%rsi, -32(%rbp)
	movl	$0, -8(%rbp)
	movl	$1, -4(%rbp)
	leaq	-4(%rbp), %rdx # RDX には y のアドレスが入る。
	leaq	-8(%rbp), %rax # RAX には x のアドレスが入る。
	movq	%rdx, %rsi # 呼び出し規約に基づいて、パラメータを RSI レジスタ渡しにしている。
	movq	%rax, %rdi # 呼び出し規約に基づいて、パラメータを RDI レジスタ渡しにしている。
	call	swap
	movl	-4(%rbp), %edx
	movl	-8(%rbp), %eax
	movl	%eax, %esi
	movl	$.LC0, %edi
	movl	$0, %eax
	call	printf
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.globl	swap
	.type	swap, @function
swap:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -24(%rbp)
	movq	%rsi, -32(%rbp)
	movq	-24(%rbp), %rax
	movl	(%rax), %eax
	movl	%eax, -4(%rbp)
	movq	-32(%rbp), %rax
	movl	(%rax), %edx
	movq	-24(%rbp), %rax
	movl	%edx, (%rax)
	movq	-32(%rbp), %rax
	movl	-4(%rbp), %edx
	movl	%edx, (%rax)
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	swap, .-swap
	.ident	"GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
	.section	.note.GNU-stack,"",@progbits
``` 

参照渡しができない言語の場合、言語仕様として参照表現がないというだけで、参照渡しは使われています。

パラメータが値渡しされる場合は、汎用レジスタに値がロードできるもの、具体的には整数や浮動小数点数が値渡しになり、汎用レジスタに値がロードできないもの、具体的には配列や一般のオブジェクトが参照渡しになります。

したがって、参照渡しされる型を利用すれば、swap 関数と同様の機能が実現できます。

つぎのコードは JavaScript で参照渡しと同様の機能を実現する例です。配列は参照渡しなので、swap 関数により２つのパラメータの中身が交換されます。

 
```JavaScript
'use strict';

const swap = (x, y) => {
    let u = x[0];
    x[0] = y[0];
    y[0] = u;
};

var a = [0];
var b = [1];

swap(a, b);

console.log("%i, %i", a, b);
```

–