Transcript CHppt\ch7

第七章
參數的傳遞
陳維魁 博士
[email protected]
儒林圖書公司
1
大綱








副程式概念介紹
副程式基本觀念
參數傳遞的方法
別名
超荷法
通用性函數
六種常見的副程式呼叫方式
精選習題
2
副程式概念介紹

副程式的元素


副程式的名稱
參數

實際參數(actual parameter)


呼叫副程式的參數串列即稱為實際參數,又稱為真
實參數。
型式參數(formal parameter)

被呼叫副程式的參數串列即稱為型式參數,又稱為
虛擬參數(dummy parameter)
3
副程式概念介紹

副程式的元素

環境(environment)


由副程式執行時所參考的全域變數(global
variable),區域變數(local variable)及其他必要的
資訊構成
副程式執行的程式段
4
範例

program demo;
var
a1,a2 : integer;
....
procedure swap(b1, b2:integer);
var
temp: integer;
begin
temp := b1;
b1 := b2;
b2 := temp
end;
begin
swap(a1, a2)
end.

左例中
swap(a1,a2)
的參數串列
(a1,a2)即為實
際參數串列,
副程式
swap(b1,b2)
中的參數串
列(b1,b2)則
為型式參數
串列
5
型式參數的語意模型

型式參數的三種不同語意模型分別為

in mode


out mode


型式參數可從對應的實際參數接收訊息。
型式參數可將訊息傳給對應的實際參數
in out mode

型式參數可從對應的實際參數接收訊息也可將訊
息傳給對應的實際參數
6
實際與型式參數的對應關係

關鍵字對應法



參數的個數較多時適用
當參數的數目很多時,採用關鍵字對應法,程
式的可讀性較高、較易維護不易出錯且可以不
必按照特定順序
位置對應法

參數的個數較少時適用(通常是5個以下)
7
範例







ADA程式語言呼叫時
Procedure DRAW_AXES(X_ORI,Y_ORI,………)
呼叫方式
1. 位置對應法
DRAW_AXES(500,500,……)
2. 關鍵字對應法
DRAW_AXES(X_ORI=>500, Y_ORI=>500,…..)
8
副程式的執行模式




副程式的執行動作是因為呼叫程式呼叫了
副程式所引起
當呼叫程式呼叫副程式時會將程式的控制
流程轉移到副程式開始處
待副程式執行結束時會將程式的控制流程
轉移到呼叫程式中呼叫副程式敘述的下一
個敘述
副程式執行時可改變其所處的環境的狀態
9
副程式的執行模式
呼叫程式
被呼叫程式
sub( )
3
1
2
4
call sub( )
*
5
6
呼叫程式執行
呼叫sub( )副程式
控制流程轉移到sub( )
副程式
sub( )副程式執行
sub( )副程式執行完畢,
控制流程轉移回呼叫
程式
呼叫程式繼續執行
10
副程式的種類

程序(procedure)


沒有傳回值
函式(function)



函式與程序類似,最大的不同之處是函式會傳
回一個值
函式本身亦具有型態
函式也可作為運算式的一部分
11
求最大公因數 程式段
Pascal
function gcd(a, b: integer):integer;
begin
if b = 0 then
gcd := a
else gcd := gcd(b, a mod b)
end;

12
求最大公因數 程式段

C
int gcd(a, b)
int a, b;
{
if (b = = 0) return (a);
else return (gcd(b, a%b));
}
13
參數傳遞的方法

常見參數傳遞的方法





傳值呼叫法
傳址呼叫法
傳名呼叫法
數值結果呼叫法
本文呼叫法
14
傳值呼叫法 (call by value)




呼叫程式呼叫被呼叫程式時,將呼叫程式
中實際參數的值,傳給被呼叫程式中對應
的型式參數
實際參數與相對應的型式參數不會佔用相
同的記憶體空間
沒有副作用(side effect)-邊際效應
只提供 in mode 參數
15
傳值呼叫法 (call by value)

優點


實際參數的值只能被讀取,在副程式執行的過
程中不會因為更改型式參數的值連帶地將實際
參數的值更改
缺點

需配置額外的記憶體空間給型式參數使用
16
傳址呼叫法 (call by address) (傳參)





又稱為call by reference或call by location
呼叫程式呼叫被呼叫程式時,將呼叫程式
中實際參數的位址,傳給被呼叫程式中對
應的型式參數
實際參數與對應的型式參數會佔用相同的
記憶體空間
有副效應-邊際效應
提供 in out mode 參數
17
傳址呼叫法 (call by address)

優點


不需配置額外的記憶體空間給型式參數使用
缺點

在副程式執行的過程中會因為更改型式參數的
值連帶地將實際參數的值更改(副作用)
18
傳名呼叫法 (call by name)

呼叫程式呼叫被呼叫程式時,將呼叫程式
中實際參數的名稱,傳給被呼叫程式中對
應的型式參數

將副程式中所有型式參數的名稱以實際參
數的名稱來替代

實際參數與對應的型式參數會佔用相同的
記憶體空間
19
傳名呼叫法 (call by name)

優點

具有較大的彈性,可提供不同型式的實際參數
讓同一個副程式做不同的工作
當實際參數為簡單變數時,傳名呼叫法之處理方
式與傳址呼叫法相同
 當實際參數為常數時,傳名呼叫法之處理方式與
傳值呼叫法類似


缺點

程式難讀、不易製作、執行速度較慢及不易學
習
20
數值結果呼叫法 (call by value result)




呼叫程式呼叫被呼叫程式時,將呼叫程式
中實際參數的值,傳給被呼叫程式中對應
的型式參數
實際參數與型式參數不會佔用相同的記憶
體位置
當被呼叫程式執行完畢時,會將被呼叫程
式中型式參數的值,傳回給呼叫程式中對
應的實際參數
提供 in out mode 參數
21
範例

設有一程式語言的副
程式如下:
PROCEDURE P(A,B,C);
B=C;
RETURN(A+C);
END;

若X=1,Y=2,試問執
行"R←P(X,X,X+Y)"
之後對於 (a)傳名呼叫
法 (b)傳值呼叫法 (c)
傳址呼叫法三種不同
的參數傳送的方式,
R之值各為若干?
22
Ans
X




1
X
X=X+Y
3
Return (X+X+Y)
Y
2
Y
2
傳名呼叫:8
註 (1)
註 (2)
C
3
C
3
 佔用相同的憶體空間。
副程式中的參數及指令中的變數都用主程式的參數取代。
PROCEDURE P(X,X,X+Y); 把名稱替換掉並且計算
B=C; -> X=X+Y=3
RETURN(A+C); -> A+C=>X+X+Y=3+3+2=8
END;
(a)
3+3+2=8
∴結果為 8
傳值呼叫:4
 X=1,Y=2 P(X,X,X+Y)
 A=1 C=3 傳回1+3=4
(c)
傳址呼叫:6 同位址參數放同位置,每個都列出來
(b)
23
範例
若右例以:
(a)傳名呼叫法
(b)傳值呼叫法
(c)傳址呼叫法
(d)數值結果呼叫法
四種不同的引數傳送的方
式,A之值各為若干?

procedure P(X,Y,Z);
begin
Y:=Y+1;
Z:=X+X;
end;
begin
A:=1;
B:=1;
P(A+B,A,A);
print A;
end.
24
Ans

(a)傳名呼叫法
 A:=1;






B:=1;
P(A+B,A,A);執行函數
Y:=Y+1; 以參數換掉A:=A+1 A值為2
Z:=X+X; A:=A+B+A+B=6 A值為6
(b)傳值呼叫法 所有參數分開看
A:=1;
B:=1;
P(A+B,A,A);執行P(2,1,1) 此時X=2, y=1 ,z=1
Y:=Y+1 =2
Z:=X+X=4
此時A還是1

25









(c)傳址呼叫法
A:=1; B:=1;
P(A+B,A,A); 執行P(X,Y,Z) 此時X=2, Y=Z=A=1同位址
Y:=Y+1; 此時Y=Z=A=2
Z:=X+X; 此時Y=Z=A=2+2
數值結果呼叫法
將值傳到函數執行 P(2,1,1) 此時X=2, y=1 ,z=1
執行Y:=Y+1 =2
Z:=X+X=4
傳回(2,2,4)=>P(A+B,A,A)
A如果取第二個是2,取第三個是4

26
本文呼叫法 (call by text)


在呼叫時不把實際參數閉合(closure),而
是一直延遲到遇到型式參數時再予閉合
用被呼叫程序環境閉合時的狀態計算實際
參數之值



“閉合”是指一程序與其相關環境繫結在一起之
過程
LISP之FEXPR版本採用此法
和傳名呼叫類似,但是名稱相同時候先用
區域參用環境的變數結合,沒有定義的話
才會與實際參數結合
27
本文呼叫法範例

var x : integer;
procedure text(y);
var x : integer;
begin
x := 5;
writeln(y);
end;



本文呼叫法處理結果為 5
傳名呼叫法處理則結果為 4
名稱相同時候先用區域參
用環境的變數結合,沒有
定義的話才會與實際參數
結合

begin
x:=4;
text(x);
end.
28
別名 (aliasing)


兩個或兩個以上名稱不同的變數,佔用相
同的記憶體空間
別名形成的原因




參數傳遞時採用傳址呼叫法
變異記錄(variant record)
指標
Fortran的“equivalence”敘述
29
別名 現象對程式執行結果的影響

已知某一算術運算式(arithmetic expression):
A×FUN(A)+A
若執行此運算式前之A值為2,FUN(A)傳
回值為3,而執行此運算式時由於副作用
(Side effect)造成A值變成6,則下列何者不
可能是此運算式執行後之值?
(A) 20 (B) 24 (C) 12 (D) 10
30





由左至右A×FUN(A)+A 2*3+6=12
由右至左A×FUN(A)+A 6*3+2=20
先導入A值 2*3+2=8
先執行副程式 6*3+6=24
如果沒有邊際效應則運算都是2*3+2=8
31
超荷法 (overloading)


如果結構(constructure),因為引數的型態
不同而具有不同的意義的話,則稱此現象
為超荷法
範例

A+B

若A,B為整數則執行整數加法運算;若A,B為
實數則執行實數加法運算
32

Algol68中關於ABS()函數 有三種定義

Abs=(real A) real: if A<0 then –A else A fi
ABS=(int B) int: if B<0 then –B else B fi
ABS=(long real C) long real: if C<0 then –C
else C fi


33

C++

Double min (double A, double B)
{
return (A<B)? A:B;
}
Int min (int C, int D)
{
return (C<D)? C:D;
}
型態不同時候,使用的函數也不同








34
通用性函數 (generic function)



允許參數傳遞的對象為“型態”
主要的用途是可排除程序對資料型態的依
賴性
優點




可減少程式碼的長度
可減少撰寫程式時,發生錯誤的可能性
代表性的語言是ADA
型態改變時候,可以根據傳遞型態,同個
函數可以根據參數,處理不同型態
35
ADA 通用性函數範例

generic
type T is private;
package P is
procedure SWAP (A, B: in out T);
end P;
package body P is
procedure SWAP (A, B: in out T) is
temp : T;
begin
temp := A; A := B; B := temp;
end;
end P;
36
ADA 通用性函數範例

procedure SWAPINT is new SWAP (integer);
procedure SWAPINT(A,B:in out integer) is
temp : integer;
begin
temp:=A;A:=B;B:=temp;
end;
37
ADA 通用性函數範例

procedure SWAPINT is new SWAP (real);
procedure SWAPINT(A,B:in out real) is
temp : real;
begin
temp:=A;A:=B;B:=temp;
end;
38
六種常見的副程式呼叫方式






呼叫∕返回副程式(call∕return subroutine)
遞迴副程式(recursive subroutine)
例外處理副程式(exception handling subroutine)
並行常式(coroutine)
排程副程式(scheduled subroutine)
並行程序(concurrent subroutine)
39
呼叫∕返回副程式


與一般的副程式呼叫是相同的
傳統的呼叫/返回副程式須滿足以下五項限
制





不允許遞迴呼叫
副程式被呼叫時,必須有明顯的呼叫敘述
副程式被呼叫時,應從頭開始執行
副程式被呼叫時,控制流程應立即轉移
單一執行順序
40
遞迴副程式


若副程式可以呼叫本身或透過其他的副程
式來呼叫本身
直接遞迴


副程式可以自己呼叫自己
間接遞迴

副程式可以透過其他的副程式來呼叫自己
41
例外處理副程式


處理例外情況的副程式
首創例外處理的語言為 PL/1
42
並行常式



一種交換關係
假設有二個程序分別是P
與Q,其中程序P可以啟動
Q的執行,而程序Q也可以
啟動程序P的執行
當程序啟動另一程序的執
行時,會先將自己目前的
狀態儲存起來,下次被啟
動執行時,會由上次執行
結束之處繼續往下執行,
而非從頭開始執行
Process P
(2)
(3)
(1)
resume Q
Process Q
(4)
resume P
(5)
43
排程副程式



一般的副程式呼叫是在被呼叫時,立即執行副程
式
排程副程式在被呼叫時,並未立即被執行而是須
在符合某種條件下才會被啟動執行
範例


CALL SUB AT TIME=100
 代表當時間為100時,副程式SUB才會被啟動。
CALL SUB1 AFTER SUB2
 代表當副程式SUB2執行完畢後,副程式SUB1才會被
啟動
44
並行程序

同時執行的程式段即稱為並行程序
45
練習

傳名呼叫法、傳值呼叫法及傳址呼叫
法在資訊定址值方面有早晚之分,試
列出早晚的順序。
46
Ans

資訊定址值方面之速度以傳址呼叫法最快
、傳名呼叫法次之而傳值呼叫法則是最慢
。
47
練習





假設我們要得到下列控制結構(control
structure)則必須分別從單純的呼叫/回歸
(simple call-return)方式中放鬆那些限制?
(1) 遞迴程序(recursive procedure)
(2) 互動程序(coroutine)
(3) 排程化的副程式(scheduled
subprogram)
(4) 平行的副程式(concurrent subprogram)
48
Ans




(1) 不允許遞迴呼叫。
(2) 副程式被呼叫時,應從頭開始執行。
(3) 副程式被呼叫時,控制流程應立即轉
移。
(4) 單一執行順序。
49
練習







請就以下不同的實際參數對傳值呼叫法及
傳址呼叫法的效果為何?
(1) 簡單變數(Simple variable)。
(2) 常數(Constant)。
(3) 運算式(Expression)。
(4) 指數為常數的陣列元素。
(5) 指數為運算式的陣列元素。
(6) 陣列。
50
Ans
簡單變數
常
傳值呼叫
傳此簡單變數之值
傳址呼叫
傳此簡單變數之位址
傳此常數之值
配置一個記憶體空間給此常數,
傳該記憶體空間之位址
數
運算式
計算運算式之值,傳此值 計算運算式之值,配置一個記
憶體空間給此值,傳該記憶體
空間之位址
求得陣列元素之值,傳此 求得陣列元素之位址,傳此位
址
指數為常數 值
的陣列元素
求得陣列元素之值,傳此 求得陣列元素之位址,傳此位
址
指數為運算式 值
的陣列元素
陣
列
通常不允許
傳陣引之起始位址
51
練習

試問在Java語言中,函數之間的參數如何
傳遞?
52
Ans


(1) 傳值呼叫法(call by value):
若參數之型態若為語言本身所提供的資料
型態,則採用傳值呼叫法來處理參數傳遞
之問題。
(2) 傳址呼叫法(call by reference):
若參數為物件、陣列等型態,則可提供傳
址呼叫法之效果來處理參數傳遞之問題。
53
練習




定義N!=N*(N-1)*(N-2)*… *2*1
例如5!=5*4*3*2*1=120
(1) 請利用Recursion的方式寫出一程式PA
來計算N!
(2) 請利用迴圈(Loop structure)的方式改
寫PA立另一程式為PB
(3) 比較PA和PB兩程式的執行效率為何?
54
Ans



(1) int PA(int N)
{
if (N = = 1) return(1);
else return (N*PA(N-1));
}
(2) PB:
f=1;
for (I=2; I<=N; I++)
f = f * I;
(3) PB效率較佳,因為採用非遞迴方式處理程式段
。
55
練習




以下仿C語言語法之主、副程式而言:
(一) 如果所有的參數都是採用傳值呼叫法,寫出執行後所印製之結
果並說明其理由。
(二) 如果所有的參數都是採用傳址呼叫法,寫出執行後所印製之結
果並說明其理由。
/* main program */
/* subprogram */
mainO
swap(a, b:integer)
{int i,j;
{int temp;
i=3; j=5
temp=a;
Swap(3,j);
a=b;
Printf("%d,"3*i+j);
b=temp;
}
}
56
Ans




採用“call by value”:
i
3
i
3
j
5
j
5
a
3
a
5
b
5
b
3
輸出結果為3×i+j=3×3+5=14。
採用“call by reference”:
i
3
i
j,b
5
j,b
3
3
a
3
a
5
3×i+j=3×3+3=12
57
練習



考慮底下以類似Pascal語言語法所定義的二函數副程式:
function A(I,K: integer):integer;
begin
if I=0 then
A : =1
Else
A:=K
end;
function B(X: integer) : integer ;
begin
B : = A(X, X * B(X-1))
end;
試以傳值呼叫法方式來計算B(5)的結果。
58
Ans

B = 5*B(4) =5*4*B(3)=5*4*3*B(2)
= 5*4*3*2*B(1)=5*4*3*2*1*B(0)
= 5*4*3*2*1*1 (∵B(0)=1)
所以,結果為120
59
練習


就執行下列仿語言語法之程式,舉出兩種可能印出
的答案,並提出合理的語意說明。
int fun(int *k)
{*k + = 2;
return 2*(*k) +1;
}
void main( )
{int i = 10, dat;
dat = fun(&i) + i;
printf(“%d”, dat);
}
60
Ans

由於副作用之影響,若“dat=fun(&i)+i;”敘
述採用左結合性求值,則結果為37;但若
“dat=fun(&i)+i;”敘述採用右結合性求值,
則結果為31。
61
練習




以下Pascal程式倘若有參數傳遞
(1)
採數值結果呼叫法則結果為何?
(2)
採引用呼叫法則結果為何?
program param(input, output);
var
a, b :integer;
procedure p(x, y: integer);
begin
x := x+2;
a := x×y;
x := x+1;
end;
begin
a:=1;
b:=2;
p(a, b);
writeln(a);
end.
62
Ans


數值結果呼叫法:a. 須額外配置記憶體空間給副程式中之型
式參數。
b. 副程式執行完後,將副程式之型式參數串列結果值傳回
給對應的主程式實際參數。其作法如下:
a
1
b 2
a
b 2
x:= x+ 2
a 6
b 2
a := x* y
a 6
b 2
a := x+ 1
x 1
x 3
x 3
x 4
y 2
y 2
y 2
y 2
x
(
(

1
4
a
y
¡D
2
¡D
b
)
writeln(a)
2
結果為 4
)
引用呼叫法:
y,a
1
y,b
2
writeln(a)
x:= x+ 2
7
y,a
3
y,b
2
a := x* y
結果為 7
y,a 6
y,b
2
x:= x+ 1
y,a 7
y,b
2
63
練習



執行下列的程式,寫出結果。
program House(output);
var thing : integer;
procedure Cheat(var hee, haw : integer);
begin
hee := -1;
haw := -hee;
end;
begin
thing := 1;
Cheat(thing, thing);
writeln(thing);
end.
Pascal程式,由於Subroutine中,型式參數以“var”宣告故採用
傳址呼叫法。
64
Ans

結果為1
haw, hee, thing
1
hee=-1
-1
haw=-hee
1
writeln(thing)
1
65
練習




考慮以下程式片段:
void main() {
int value=2,list=5;
swap(value,list);
}
void swap(int a, int b){
int temp;
temp=a;
b=temp;
}
(1) 若參數傳遞為以值傳遞,則在main()執行完
swap(value,list)之後,變數value,list之值各為多少?
(2) 若參數傳遞為以址傳遞,則在main()執行完
swap(value,list)之後,變數value,list之值各為多少?
66
ANS

採用call by value法,作法如下:
所以,執行完swap(value,list)後,value=2,list=5。

採用call by address法,作法如下:
所以,執行完swap(value,list)後,value=2,list=2。
67
練習



有一程式如下:
program parms(output);
Var i, j : integer;
a : array[1..4] of integer;
procedure P(x, y : integer);
begin
i:=i+1; j:=j+1;
writeln(x, y);
x:=x+1; y:=y+1
end;
begin
for i:=1 to 4 do a〔i〕:= i;
i:=1; j:=2;
P(a[j], a[i]);
writeln(i, j, a[1], a[2], a[3], a[4])
end
請問以(1)Call by reference (2)Call by value (3)Call by name法傳
遞參數,程式執行結果為何?
68
Ans



(1) call by reference:傳址
21
232334
(2) call by value:
21
231234
(3) call by name:傳名
32
231344
69
練習






假設有一ALGOL-LIKE程式片段如下,請依以下所列之各種參數傳遞
方式分別寫出此程式片段之執行結果。
(1) Call by value
(2) Call by address
(3) Call by name
(4) Call by value-result
program Parm(output);
var x: integer;
procedure sub(exp, I : integer)
begin
I := exp;
I := exp;
end;
begin /* main program */
x := 3;
sub(2*x, x);
print(x);
end.
70
Ans




I, x
(1) Call by value:
∴結果為3
(2) Call by address:
∴結果為6
3
I:=exp
I, x
6
I:=exp
I, x
6
print (x)
6
exp
6
exp
6
exp
6
71
Ans




3)
a.
b.
c.
Call by name:
名稱對應關係:exp=>2*x, I=>x
名稱替換:I:=exp=>x:=2*x, I:=exp=>x:=2*x
執行:

∴結果為12
(4) Call by value-result:

∴結果為6

72
練習

對應參數的位址方法,若以應用於下列程式片段,試問值為
何?
Main
real L,M,N
L=5
M=2
N=S(L,2L,L+2M)
Output N
End(Main)
S:sub(X,Y,Z)
real X,Y,Z,W
Y=Z
W=Y+Z
return(W)
end(sub)
73
Ans
X, L
5
X, L
5
M
2
M
2
Y
10
Y
Z
9
Z
Y=Z
X, L
5
M
2
9
Y
9
9
Z
9
W
18
W=Y+Z
return (W)
N
18
74