見出し画像

[cocoa][swift]え!それって参照渡し?

# はじめに
とても違和感を感じる用語がある。それは、『参照渡しだ』。
え!違うだろ?と感じていたが、いい機会なので、調べてみた。

# 引数
引数と呼ばれるものにどんな種類があるのか、まずは列挙してみる。

## 実引数
関数に渡す値。

sum(1, 2);

## 仮引数
関数が受け取った変数。

int sum(int a, int b){
    return a + b;
}

## 値渡し(call by value
値が渡される。

## ポインタ渡し
渡す値がアドレスの値渡し。

## 参照渡し
### 変数渡し(call by variable)

変数そのものを渡す。

### 参照渡し(call by reference)
参照渡しで、内部でアドレス情報を渡す方法。

自分の理解では、初期のプログラミング言語ではローカル変数の実装が困難で、変数はグルーバル。なので、変数渡し。近年の高級言語でローカル変数が実現され、その流れで値渡しが用意されたのかなと思っている。

# Pascal
Pascalでは,値渡し(call by value)と変数渡し(call by variable)が存在し、変数渡しは参照渡しに相当する。

そもそもは、変数渡しの実装方法に参照渡しがある。参照渡しは、変数に対する参照(アドレス情報)を渡す方法だ。

Inside Macintoshは、コードはPascalで記載されているが、それをC言語で利用する場合、varがついた変数渡しの引数は、C言語ではポインターと読み替えていた。

具体的には、Inside Macintoshで以下のようにPascalで説明されていたとする。

PROCEDURE GetPort(VAR port: GrafPtr);

これをC言語では、以下のように読み替える。

void GetPort (GrafPtr *port);

# C言語
C言語の関数の引数は全て値渡し。K&Rでしっかりと説明されている。
ポインタでアドレスの値を渡すのを参照渡しと呼ぶのは間違いだ。

# Java
Javaも全て値渡し。
ポインタ演算ができない、アドレス(参照)の値渡しが利用できるが、これを参照渡しと呼ぶのは、如何なものか。

0'RellyのJavaクイックリファレンスでは、Javaは配列とオブジェクトを参照を通じで扱うと説明されていた。また、参照渡しという言葉と混同しないようにと書かれていた。
参照型の値渡しが参照渡しと勘違いされるのは、仮引数の参照型の中身の値の変更が、実引数側に反映されるからだと思うが、参照先自体の変更ができない。これは、C言語で、引数で渡されたポインターが指す先の値は変えらるがアドレスは変えられない。変えたい場合は、ポインターのポインタを利用するしかないということから分かると思う。

static char a[] = "hello";
static char b[] = "world";
void set_b(char **handle)
{
   *handle = b;
}
in t main(int argc, char *argv[])
{
   char *ptr = a;
   printf("%s\n", ptr);
   set_b(&ptr);
   printf("%s\n", ptr);
   return EXIT_SUCCESS;
}

# C++
値渡しに加え、本物の参照渡しが存在する。

void time_two(int& a)
{
   a *= 2;
}

また、C++11では右辺値参照・ムーブセマンティクスという所有者の移動が用意されている。

# C#
二つの参照渡しの方法が用意されている。

## ref

int initializeInMethod = 0;  // 初期化が必須
OutArgExample(initializeInMethod);
Console.WriteLine(initializeInMethod);
void OutArgExample(ref int number)
{
   number = 44;
}

## out


int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod);
void OutArgExample(out int number)
{
   number = 44;  // 代入が必須
}

refは変数が渡されるので、初期化によって値が設定されていない変数はNGだ。

outは値を返すという意味から、初期化は必須でないか、関数内で値を必ず設定しないといけない。

# Swift
値渡しだが、inoutキーワードをつけると値呼びの結果返し(call-by-value-result)となる。
C言語のポインターの場合と同様に、変数が参照型の場合、参照の値渡しにより値を変更できる。

class Demo {
   public var num = 0;
}
func set999(a: Demo) {
   a.num = 999;
}
let demo = Demo()
set999(demo)

値呼びの結果返しの例。

func set999(a: inout Int) {
   a =999
}
var num = 0
set999(&num)

少し複雑に感じるが、基本、C言語と同様と考えると成る程だ。

【関連情報】
- Cocoa Advent Calendar 2018
Cocoa.swift 2019-01
- Cocoa.swift
- Cocoa勉強会 関東
- Cocoa練習帳
- Qiita

この記事が気に入ったらサポートをしてみませんか?