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