引数
出典: フリー百科事典『ウィキペディア(Wikipedia)』
引数(ひきすう、argument = 実引数、parameter = 仮引数)とは、プログラミング言語においてサブルーチンを呼び出すときに、そのサブルーチンを実行するために渡す値のこと。仮引数と実引数の総称である。
目次 |
[編集] 仮引数
仮引数 (かりひきすう、argument) とは、サブルーチンの定義の際に、サブルーチンが実際に呼び出される時に渡されるであろう引数に名前を付け、定義内で変数として扱えるようにしたもののことである。例としてC言語系言語における定義を挙げる:
int sum(int addend1, int addend2) { return addend1 + addend2; }
上の定義では、
int
型の仮引数addend1
int
型の仮引数addend2
2つを伴った関数 sum
を定義している。定義の中で addend1
と addend2
が変数のように使用されていることに注目されたい。
[編集] 実引数
実引数 (じつひきすう、parameter) とは、サブルーチンの呼び出しの際に実際にサブルーチンに渡す値のことである。C言語系言語において前に示した例中の関数 sum
を用いた例を挙げる:
sum(123, 456);
上の文は、
- 仮引数
addend1
に対応する実引数 123 - 仮引数
addend2
に対応する実引数 456
2つを関数 sum
に渡している。因みに、上の文の結果は 579 である。
[編集] 値渡し
値渡し (あたいわたし、pass by value)は新たに値のコピーを作って渡す方法で、元の内容と同じだが独立した新たな変数が関数内に用意される。そのため外から何らかの変数を渡したとしても、実際に渡るのは変数の値のみであり、元々の変数が変更される事は起こらない。
これは「関数が副作用を持たない」という観点から、計算を中心とする言語では望ましい動作といえる。またそもそも代入概念の存在しない関数型言語では、引数は必ず値で渡されると考えられる(ただし、代入が存在しない以上コピーをとる必要もない)。
値渡しを採用した言語としてはC、ML、APL、Scheme、Java等が挙げられる。
[編集] ポインタ渡し
C言語やC++においてポインタは特定の変数を参照するメモリアドレスであり参照渡しの参照と似た性質を持つので、とある変数を参照するポインタを値渡しすると、値渡しでありながら その変数を参照渡しした時と似たような機能を実現する事もできる。このため、ポインタを値渡しする事を 単なる値渡しと区別して俗に ポインタ渡し などと呼ぶ事もある。
[編集] 参照渡し
参照渡し (さんしょうわたし、pass by reference) は変数そのものに対する参照を渡す方法で、この場合は引数に対する操作がそのまま渡された変数に影響する。
原始的な言語であるFORTRANは機械語のアドレス操作を反映した参照渡ししか持たなかった。他に参照渡しをサポートする言語としては、Pascal、Perl、C++、C#などが挙げられる。
参照渡しで言うところの「参照」と呼ばれているものと、特定の言語で「参照」と呼ばれているものが必ずしも同じでない事には注意が必要。例えば、Java は参照型を扱うための『Java の「参照」』を持つが、これは『参照渡しの「参照」』とは概念が微妙に違うため、『Java の「参照」』を渡しても参照渡しであるとは言えない。JavaHouse-Brewers の議論を参照 これは、Javaの参照型と似た参照型と、Javaのプリミティブ型に近い値型を持つC#を見ると理解しやすいだろう。C#では、特に指定しなければ参照型も値型も値渡しされるが、引数に ref
もしくは out
を使用する事によって参照渡しにする事ができる。『C#の値型』を渡すから値渡し、『C#の参照型』を渡すから参照渡しとはならない。
[編集] 値渡しと参照渡しの関係
C言語は原則として値渡しのみをサポートするが、ポインタ(メモリアドレス)によって参照された値をとり出す事が可能であるため、変数へのポインタを渡す事で元の変数を変更できる。特に初期のCは浮動小数点数を除けば、全ての値はint互換であると考えられており、オフセット計算により構造体の一部分を参照するコードも容易に記述できる。
しかしこれは、実際の変数領域を逸脱した部分をも参照できるので、あくまでも値渡しによる参照渡しのエミュレートである。参照渡しをサポートする言語でも内部的には同様の操作を行っているが、それは何らかの意味で言語の保護下にある参照となる。
- 例:Pascalの参照渡し
- Pascalの手続き(procedure)や関数(function)では、原始型(integer, realなど) の値渡しと参照渡しのどちらでも行える。参照渡しの場合は手続き・関数の引数にvarを付ける。
{ 手続き swap 内で a,b の値を入れ替える。 sampleの i,j は参照渡しされ、aとi、bとjは同じアドレスを指して いるので、i,jの値は入れ替わる。 } procedure swap(var a,b:integer); { var をつけると参照渡し } var tmp:integer; begin tmp := a; a := b; b := tmp end; procedure sample(); var i, j:integer; begin i := 5; j := 10; swap(i, j); ... { iは10, jは5になる } end;
[編集] 遅延評価
- 詳細は遅延評価を参照
Haskellなどの遅延評価型関数言語に見られる形態で、値が実際に必要になるまで計算を行わない方法。概念上は、計算方法を遅延したthunkと呼ばれるオブジェクトが渡っていると考えられる。
[編集] 名前渡し
ALGOLで採用されていた特徴的な機能の一つである。名前渡しでは値でも参照でもなく、式がそのまま渡される。基本的には参照渡しのように振る舞うが、式を参照するごとに値を計算して取り出す事が特徴である。次のような例は名前渡しに特徴的な動作と言われる。
swap(x,y) { tmp = x; x = y; y = tmp; }
この例に対し、x=i, y=a[i]
という"式"を渡すとする。仮にi=2だったとすると、
tmp = x;
- x=i=2 なのでtmpは2になる
x = y;
- xはiを渡されているのでiがyの値になる。yはa[i]だから、iはaの2番目の値になる
y = tmp;
- yはaのi番目の値だが、前手順によりiはa[2]になっている。従ってy=a[a[2]]になる。
このような複雑さもあって、ALGOL以外で名前渡しが採用された事例はほとんどない。