1
2009-7-29
§ 1 函数子程序
§ 2 子例程子程序
§ 3 实参和虚参之间的数据传送
§ 4 程序举例第 11章 子程序
2
子程序是相对于主程序而言的,到目前为止,
我们涉及到的程序都只有主程序。
一个 Fortran程序可以由 一个主程序 和 若干个子程序 组成。 程序中可以只有主程序而没有子程序,但不能没有主程序而只有子程序 。
程序在运行时,总是 从主程序开始执行,由主程序调用子程序,最后 在主程序中结束整个程序的运行 。
引言
3
Fortran的 子程序 有三种,函数子程序,子例程子程序,数据块子程序 。
通常我们将函数子程序和子例程子程序统称为子程序,数据块子程序仅用于给 公用区中的变量赋初值。本章提到的“子程序”即指函数子程序或子例程子程序。
子程序可以被主程序调用,也可以被其它子程序调用。但 标准 Fortran77的子程序不能直接或间接地调用自己,即 不允许递归调用 。
4
2009-7-29
§ 1 函数子程序 (p234-238)
【 例 11.1】 计算函数,的值。


)(
)(
)(
01
00
01
x
x
x
y
【 引例 】 求函数 f(x)=x2+x+1在 x =1,2,3时的值。
函数用一条语句即可实现,既可采用语句函数的形式,
也可采用函数子程序的形式。
write(*,*)f(1.0),f(2.0),f(3.0)
end
function f(x)
f=x*x+x+1
end
函数用一条语句无法实现,只能采用函数子程序的形式。
§ 1 子程序
5
2009-7-29
function y(x)
if(x.gt.0.0) then
y=1.0
else if(x.eq.0.0) then
y=0.0
else
y=-1.0
end if
end
write(*,*)‘input x’
read(*,*)x
write(*,*)‘y=’,y(x)
end
主程序:函数子程序:
§ 1 子程序
①②


6
2009-7-29
一、函数子程序的定义定义形式:
注:
⑴ 函数名既是该函数的名字又代表该函数的函数值,
故 函数名有类型,其类型既可隐式说明也可显式定义。
⑵ 函数名后圆括号中为函数的虚参,多个虚参用逗号隔开,没有虚参时括号不能省略。
虚参的类型既可隐式说明也可显式定义。 显式定义时,
虚参的类型定义语句是 放在函数体中 的 。
虚参可以是变量、数组或子程序名。
[类型说明 ] function 函数名 (虚参 )
end
函数体函数头,也称为 function语句
§ 1 子程序
7
2009-7-29
一、函数子程序的定义定义形式:
注:
⑶ 函数体中语句排列顺序与主程序相同,即说明语句在前,执行语句在后。
一个程序单元(主程序或子程序)中语句的具体顺序参见教材 p328附录 Ⅱ 。
[类型说明 ] function 函数名 (虚参 )
end
函数体函数头,也称为 function语句
§ 1 子程序
8
2009-7-29
§ 1 子程序注释行
program,function,subroutine,block data语句
format
语句
parameter语句 implicit语句其它说明语句
data语句 语句函数语句 可执行语句
end语句附录 Ⅱ 一个程序单元中语句的顺序
parameter语句 可以任意地与 implicit语句 和 其它说明语句 交替出现。
data语句 可以任意地与 语句函数语句 和 可执行语句 交替出现。
9
2009-7-29
一、函数子程序的定义定义形式:
注:
⑷ 函数体下面的 end语句表示结束本子程序,返回到调用它的程序单元。
若在函数体中间返回,需用 return语句 。
⑸ 在返回前,须将需要返回的计算结果赋给函数名。
即 函数名至少要在函数体中被赋值一次 。
赋值形式,函数名 =计算结果
[类型说明 ] function 函数名 (虚参,…)
end
函数体函数头,也称为 function语句
§ 1 子程序
10
2009-7-29
⑹ 函数子程序是一个 独立 的程序单元,函数体内所用的变量、语句标号等的作用范围仅局限于本子程序内。
每个函数子程序仅通过函数名和虚参 (或公用区 )与其它的子程序以及主程序建立联系。
小结,Fortran77中有三种形式的函数
内部函数:
语句函数:
函数子程序,即 外部函数:
是 Fortran系统自己提供的函数,如,abs、
sqrt等函数。
用一个语句定义的函数,只能被其所在的程序单元使用。
用户定义的子程序,可以被其它子程序、主程序调用,但不能自己调用自己。
§ 1 子程序
11
2009-7-29
二、函数子程序的调用与调用 Fortran系统内部函数的形式相同,即在需要调用函数子程序的地方直接写上 函数名 (实参 )或者 函数名 ( )。
在调用时,实参的数据将传送给虚参。
在 Fortran语言中,在调用子程序(包括函数和子例程)时,实参向虚参的数据传递仅采用 地址传递 的形式,
从而使虚参和实参指向同一个存储单元 。
在子程序中对虚参值进行改变,将导致相应的实参值也发生变化。
§ 1 子程序
12
2009-7-29
function y(x)
if(x.gt.0.0) then
y=1.0
else if(x.eq.0.0) then
y=0.0
else
y=-1.0
end if
x=0
end
write(*,*)'input a'
read(*,*)a
write(*,*)'y=',y(a)
write(*,*)'a=',a
end
进一步,子程序中需要返回的计算结果不仅可以通过函数名返回,也可以通过与虚参相对应的实参返回。
§ 1 子程序
13
2009-7-29
function y(x,z)
if(x.gt.0.0) then
y=1.0
z=1.0
else if(x.eq.0.0) then
y=0.0
z=0.0
else
y=-1.0
z=-1.0
end if
end
write(*,*)'input a'
read(*,*)a
write(*,*)'y=',y(a,b)
write(*,*)‘b=',b
end
地址传递的详细情况将在本章第 3节中进行介绍。
§ 1 子程序
14
2009-7-29
§ 2 子例程子程序 (p239-242)
§ 2 子例程子程序一、子例程子程序的定义定义形式:
二、子例程子程序的调用必须用一条独立的 call语句 调用子例程子程序。
形式:

subroutine 子例程程序名 (虚参 )
end
子程序体
call 子例程程序名 (实参 )
call 子例程程序名 ( )
15
2009-7-29
子例程子程序与函数子程序的 区别,
函数子程序的名字可以返回子程序中的一个计算结果,因此 函数名是有类型的 。
子例程子程序的名字 只是一个子程序的名字,
不代表任何值,它 没有类型,因此不能通过子例程程序名向调用程序返回一个计算结果。
若子例程子程序中需要向调用函数返回结果,
可通过与虚参相对应的实参返回 。
§ 2 子例程子程序
16
2009-7-29
函数子程序、子例程子程序使用的 基本准则:
当子程序 不需要返回值 时,使用子例程子程序。
当子程序 仅需要返回一个值 时,使用函数子程序,利用函数名进行返回。
当子程序 需要返回多个值 时,一般使用子例程子程序,利用实参进行返回。
【 11.2】 编写子例程子程序分别求出 4× 4矩阵两个对角线上元素之和。
两个对角线指 主对角线,次对角线 。
§ 2 子例程子程序
17
2009-7-29
§ 2 子例程子程序
subroutine sum(a,s1,s2)
real a(4,4)
s1=0
do 10 i=1,4
s1=s1+a(i,i)
10 continue
s2=0
do 20 i=1,4
s2=s2+a(i,5-i)
20 continue
end
18
2009-7-29
§ 2 子例程子程序
real w(4,4)
write(*,*)'请输入一个 4× 4的矩阵,'
read(*,*)( (w(i,j),j=1,4),i=1,4)
call sum(w,x1,x2)
write(*,*)'主对角线元素之和 =',x1
write(*,*)'次对角线元素之和 =',x2
end
19
2009-7-29
§ 3 实参和虚参之间的数据传送 (p242-249)
函数子程序和子例程子程序的虚参可以是 变量名,数组名,子程序名 。
在子程序(函数子程序和子例程子程序)被调用之前,所有虚参均无定义( 既不占有存储单元也没有具体的值 ),只是起到形式上的作用,故虚参也称为 形参(形式参数) 。
当子程序被调用时,实参和虚参 按地址结合,
这时实参的存储单元就是对应虚参的存储单元,
虚参值改变,相应的实参值也改变。
当子程序执行完退出后,虚参又变成无定义的。
§ 3 实参和虚参之间的数据传送
20
2009-7-29
实参和虚参之间的数据传递分三种情况讨论:
一、变量作为虚参二、数组作为虚参三、子程序名作为虚参
§ 3 实参和虚参之间的数据传送
21
2009-7-29
一、变量作为虚参当虚参是变量时,对应的实参可以是 同一类型 的常量、
变量、数组元素或表达式。
1,实参是变量或数组元素实参和虚参之间按地址结合。
【 11.3】
§ 3 实参和虚参之间的数据传送
subroutine sub(x,a)
integer x,a
a=2*x
x=2*a
end
integer a,c(3)
data c/3*0/
a=100
call sub(a,c(2))
write(*,*)'a=',a,'c(2)=',c(2)
end
22
2009-7-29
100
§ 3 实参和虚参之间的数据传送
subroutine sub(x,a)
integer x,a
a=2*x
x=2*a
end
integer a,c(3)
data c/3*0/
a=100
call sub(a,c(2))
write(*,*)'a=',a,'c(2)=',c(2)
end
a主程序:
0 00
c(1) c(2) c(3)
子程序,x a
200400
23
2009-7-29
一、变量作为虚参当虚参是变量时,对应的实参可以是 同一类型 的常量、
变量、数组元素或表达式。
2,实参是常量或表达式虚参可以被使用,但不能被改变,否则程序运行将出错。
如,call sub(100,1+100)
end
subroutine sub(x,a)
integer x,a
a=2*x
x=2*a
end
常量或表达式不会被分配存储单元。
这种情况下,虚参由系统分配存储单元,该存储单元只能读不能写,子程序结束后存储单元被系统释放。
§ 3 实参和虚参之间的数据传送
×
24
2009-7-29
特别注意:
当虚参为字符型变量时,虚参的长度定义应遵循以下两条规则之一:
⑴ 虚参字符变量的长度必须小于等于对应实参变量的长度。
⑵ 虚参字符变量的长度可用 (*)来定义,表示长度不定。当调用子程序时,该虚参变量的长度自动与对应实参的长度相同。
§ 3 实参和虚参之间的数据传送
25
2009-7-29
character str1*8,str2*40
data str1/'china'/,str2/'china'/
call sub(str1)
call sub(str2)
end
subroutine sub(ch)
character ch*(*)
write(*,100)ch,len(ch)
100 Format(1x,'字符串为 ',a,',长度 =',i2 )
end
§ 3 实参和虚参之间的数据传送
【 11.4】
26
2009-7-29
§ 3 实参和虚参之间的数据传送二、数组作为虚参当虚参是数组时,对应的实参可以是 同一类型 的数组名或数组元素。
数组作为虚参可分为两种情况,虚参是非字符型数组
(包括数值型和逻辑型),虚参是字符型数组 。
1,虚参是 数值型或逻辑型数组,对应的实参是同一类型的数组名或数组元素。
实参是数组名:
在调用子程序时,将把实参数组第一个元素的地址传送给子程序作为虚参数组第一个元素的地址,从而导致它们占用同一个存储单元。
从第二个元素开始依次类推,虚参数组的各元素与实参数组的各元素按照 存储顺序 一一对应结合。
27
2009-7-29
a(1) a(2) a(3) a(4) a(5) a(6) a(7) a(8)
b(0) b(1) b(2) b(3) b(4) b(5)b(-1)
§ 3 实参和虚参之间的数据传送
dimension a(8)
call sub(a)
……
end
subroutine sub(b)
dimension b(-1:5)
……
end
28
2009-7-29
dimension a(2,4)
call sub(a)
……
end
subroutine sub(b)
dimension b(6)
……
end
a(1,1) a(2,1) a(1,2) a(2,2) a(1,3) a(2,3) a(1,4) a(2,4)
b(1) b(2) b(3) b(4) b(5) b(6)
§ 3 实参和虚参之间的数据传送
29
2009-7-29
dimension a(3,3)
call sub(a)
……
end
subroutine sub(b)
dimension b(2,2)
……
end
a(1,1) a(2,1) a(3,1) a(1,2) a(2,2) a(3,2) a(1,3) a(2,3) a(3,3)
§ 3 实参和虚参之间的数据传送
b(1,1) b(2,1) b(1,2) b(2,2)
30
2009-7-29
§ 3 实参和虚参之间的数据传送二、数组作为虚参数组作为虚参可分为两种情况,虚参是字符型数组,
虚参是非字符型数组 (包括数值型和逻辑型)。
1,虚参是 数值型或逻辑型数组,对应的实参是同一类型的数组名或数组元素。
实参是数组名
实参是数据元素在调用子程序时,将把该数组元素的地址传送给子程序作为虚参数组第一个元素的地址,从而导致它们占用同一个存储单元。
虚参数组的其余元素将与实参数组该元素后的各元素按照 存储顺序 一一对应结合。
31
2009-7-29
a(1) a(2) a(3) a(4) a(5) a(6) a(7) a(8)
b(1) b(2) b(3) b(4)
§ 3 实参和虚参之间的数据传送
dimension a(8)
call sub(a(3))
……
end
subroutine sub(b)
dimension b(4)
……
end
32
2009-7-29
dimension a(3,3)
call sub(a(1,2))
……
end
subroutine sub(b)
dimension b(4)
……
end
a(1,1) a(2,1) a(3,1) a(1,2) a(2,2) a(3,2) a(1,3) a(2,3) a(3,3)
§ 3 实参和虚参之间的数据传送
b(1) b(2) b(3) b(4)
33
2009-7-29
a(1) a(2) a(3) a(4) a(5) a(6)
b(1) b(2) b(3) b(4) b(5) b(6)
§ 3 实参和虚参之间的数据传送
dimension a(6)
call sub(a(3))
……
end
subroutine sub(b)
dimension b(6)
……
end
虚参数据的元素超出对应实参数组的范围,
运行时将出错。
34
2009-7-29
§ 3 实参和虚参之间的数据传送二、数组作为虚参数组作为虚参可分为两种情况,虚参是字符型数组,
虚参是非字符型数组 (包括数值型和逻辑型)。
1,虚参是 数值型或逻辑型数组,对应的实参是同一类型的数组名或数组元素。
实参是数组名
实参是数据元素注意,定义虚参数组时,其元素不能超过对应实参数组的范围。
35
2009-7-29
§ 3 实参和虚参之间的数据传送二、数组作为虚参数组作为虚参可分为两种情况,虚参是字符型数组,
虚参是非字符型数组 (包括数值型和逻辑型)。
1,虚参是 数值型或逻辑型数组,对应的实参是同一类型的数组名或数组元素。
2,虚参是 字符型数组,对应的实参必须是字符型的数组名或数组元素。
虚参和实参数组不是按数组元素的顺序一一对应结合,
而是按字符位臵一一对应结合。
注意,虚参数组的字符总个数不能超过对应实参数组的范围。
36
2009-7-29
character*4 b(6)
call sub(b)
……
end
§ 3 实参和虚参之间的数据传送
b(1) b(2) b(3) b(4) b(5) b(6)
subroutine sub(c)
character*3 c(4)
……
end
c(1) c(2) c(3) c(4)
37
2009-7-29
character*4 b(6)
call sub(b(2))
……
end
§ 3 实参和虚参之间的数据传送
b(1) b(2) b(3) b(4) b(5) b(6)
subroutine sub(c)
character*5 c(4)
……
end
c(1) c(2) c(3) c(4)
38
2009-7-29
§ 3 实参和虚参之间的数据传送二、数组作为虚参数组作为虚参可分为两种情况,虚参是字符型数组,
虚参是非字符型数组 (包括数值型和逻辑型)。
1,虚参是 数值型或逻辑型数组,对应的实参是同一类型的数组名或数组元素。
2,虚参是 字符型数组,对应的实参必须是字符型的数组名或数组元素。
当数组作为虚参时,推荐使用的方法,可调数组 。
可调数组的含义,将虚参数组每一维的上下界也定义为虚参,其值通过具体的实参传递过来。
39
2009-7-29
real a(9),b(4,3)
call sub(a,9,b,4,3)
……
end
subroutine sub(x,n,y,m1,m2)
real x(n),y(m1,m2)
……
end
§ 3 实参和虚参之间的数据传送可调数组的好处,当主程序中数组大小改变时,子程序不需要修改,只需要改变相应的实参值即可。
40
2009-7-29
character a(9)*5,b(4,3)*6
call sub(a,9,b,4,3)
……
end
subroutine sub(x,n,y,m1,m2)
character x(n)*(*),y(m1,m2)*(*)
……
end
§ 3 实参和虚参之间的数据传送强调,可调数组的形式只能用于子程序的虚参中,子程序定义的可调数组,其 数组名 和各 维上下界值 必须出现在虚参表中。
41
2009-7-29
§ 2 子例程子程序
subroutine sum(a,n,s1,s2)
real a(n,n)
s1=0
do 10 i=1,n
s1=s1+a(i,i)
10 continue
s2=0
do 20 i=1,n
s2=s2+a(i,n-i+1)
20 continue
end
【 11.5】
42
2009-7-29
§ 2 子例程子程序
real w(4,4)
write(*,*)'请输入一个 4× 4的矩阵,'
read(*,*)( (w(i,j),j=1,4),i=1,4)
call sum(w,4,x1,x2)
write(*,*)'主对角线元素之和 =',x1
write(*,*)'次对角线元素之和 =',x2
end
43
2009-7-29
三、子程序名作为虚参子程序的虚参可以是函数子程序名或子例程子程序名。
问题,在编写子程序时,如何说明一个虚参是子程序名?
答案,不用说明,Fortran编译系统将根据该虚参在子程序中的上下文关系确定其是一个子程序名而不是一个变量名或数组名。
【 如 】
§ 3 实参和虚参之间的数据传送
subroutine sub(a,n,x,f)
dimension a(n)
……
y=f(i)+1
x=a(i)+1
……
end
44
2009-7-29
⑴ 当子程序的虚参是一个子程序名时,它只是一个虚构的名字,并不代表实际存在的某个函数子程序名或子例程子程序名。
⑵ 只有在调用时,通过实参才能给它传递一个实际存在的子程序名。
如果该虚参是一个函数子程序名,调用时要求相应的实参是一个实际存在的外部函数名或 Fortran内部函数名。
§ 3 实参和虚参之间的数据传送
subroutine sub(x,p)
……
call p
……
end
45
2009-7-29
⑴ 当子程序的虚参是一个子程序名时,它只是一个虚构的名字,并不代表实际存在的某个函数子程序名或子例程子程序名。
⑵ 只有在调用时,通过实参才能给它传递一个实际存在的子程序名。
如果该虚参是一个函数子程序名,调用时要求相应的实参是一个实际存在的外部函数名或 Fortran内部函数名。
如果该虚参是一个子例程子程序名,调用时要求相应的实参是一个实际存在的子例程程序名。
⑶ 调用时,当 实参 是一个外部函数名或子例程程序名时,需对该实参用 external语句 进行说明;当实参是一个内部函数名时,需对该实参用 intrinsic语句 进行说明。从而表明该实参是一个子程序名而不是一个变量名。
§ 3 实参和虚参之间的数据传送
46
2009-7-29
【 11.6】 编写一个函数子程序,通过函数名的传递,使其既能计算正切值又能计算余切值。
intrinsic sin,cos
write(*,*)'请输入一个角度,'
read(*,*)x
x=3.14159*x/180
y1=f(sin,cos,x)
y2=f(cos,sin,x)
write(*,*)'正切值 =',y1
write(*,*)'余切值 =',y2
end
function f(f1,f2,x)
f=f1(x)/f2(x)
end
§ 3 实参和虚参之间的数据传送
47
2009-7-29
§ 3 实参和虚参之间的数据传送子程序名作虚参的小结:
当 子程序的虚参是一个子程序名时,不需要对该虚参进行说明,Fortran系统会自动确定该虚参名是否是一个子程序名。
当子程序的虚参是一个子程序名时,它只代表一个虚构的名字;只有在调用时,通过实参才能给它传递一个实际存在的子程序名。
在调用时,当实参是一个外部函数名或子例程程序名时,需用 external语句对该实参进行说明;当实参是一个内部函数名时,需用 intrinsic语句对该实参进行说明。
48
2009-7-29
§ 4 程序举例自学教材 p255~ 257例 11.12、例 11.13、例 11.14。
49
2009-7-29