




已阅读5页,还剩26页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第7章 模块化程序设计71 程序的模块化思想引例: 输入M、N,计算并输出:源程序(I)READ(*,*) M, NDO 5 WHILE(M.LT.N)READ(*,*) M,N 5 CONTINUEX=MT=1.0DO 10 I=1,XT=T*I10 CONTINUES=TX=NT=1DO 20 I=1,X T=T*I20 CONTINUES=S/TX=M-NT=1DO 30 I=1,X T=T*I30 CONTINUES=S/TWRITE(*,*) SEND源程序(II) FUNCTION FA(N) T=1.0 DO 10 I=1,N T=T*I 程序段1 10 CONTINUE FA=T END READ(*, *)M, N DO 5 WHILE(M.LT.N) READ(*,*) M,N 5 CONTINUE 程序段2 S=FA(M)/(FA(N)*FA(M-N) WRITE(*,*) S END源程序(I)中,加粗的3段完全相同,我们可以将其提炼出来,独立写成一个程序单位,构成一个子程序单元,写成源程序(II)的形式,这样的程序就形成了主程序和子程序的多部分、多模块的格局。一般而言,模块是指可以独立存盘,并且可以分别编译的源程序文件,是一种构成FORTRAN程序的独立成分。即是说子程序与主程序可以分别编辑存盘、编译,此时即形成所谓的多模块程序。程序模块化的优点是:(1)体现了算法和功能上的模块化,符合人们解决复杂问题的一般思路,也为结构化程序设计提供了有力支持。(2)使复杂的软件开发工作可以化整为零,便于多人分工协作,减少开发人员之间的相互干扰与重复劳动,从而有利于缩短开发周期,节省开发费用,提高软件质量。(3)可以设计一块,调试一块,设计完成,调试也随之完成,能够方便有效地防止数据之间的相互干扰,增加整个系统的稳定性与可靠性。(4)结构灵活便于组装,条理清晰便于理解与维护。其优越性随着程序规模的扩大而愈加明显。72 FORTRAN语言的三种子程序形式7.2.1函数子程序1)函数子程序定义的一般形式:类型说明符 FUNCTION 函数名(形参表) 函数名=表达式END或者:FUNCTION 函数名(形参表) 类型说明符 函数名函数名=表达式END2)对函数子程序的说明函数子程序名应该是标识符。函数子程序名一方面在程序中惟一标识一个函数子程序,因此它不能与本程序单位以及引用它的程序单位中任何其他名字相同;另一方面函数子程序名也用来返回计算的结果,因此它是有类型的。函数子程序名的类型也就是函数子程序返回值的类型。其类型说明可以采用上面的两种方式进行显式说明,否则它也将遵循隐含类型说明规则。函数子程序的返回值由函数子程序名带出,因此在子程序单元中一般会给函数子程序名赋值。但是在子程序单元中也可以不给函数子程序名赋值,当然也可以对函数子程序名进行多次赋值。函数名后的一对括号中的形参表给出了函数子程序的所有形式参数,它们是函数子程序的自变量。形式参数(简称形参或虚参)可以是变量名、数组名和子程序名等。形参表中可以有零到多个形参,当没有形参时,括号可以省略;当有多个形参时,形参之间用逗号分隔,并且在它所在的函数子程序中的命名必须是惟一的。形参也是有类型的,所以应该在函数子程序中对形参进行说明,其说明方式与简单变量的说明相同,并且同样的遵循隐含类型说明规则。由于可以将函数子程序单独放在一个文件中独立编译,以便于任何其他程序单位的引用,所以实际应用中将它与内部函数对应,称其为外部函数。3)函数子程序的引用 只能通过表达式实现对函数子程序的(引)调用。调用时采取实参(即实在参数,代表确定的、具体的值)代替形参的方式,这与内部函数、语句函数的引用方法相同。引用函数子程序时,实参的类型、个数必须与形参的一致。当引用无形参的函数子程序时,函数子程序名后的一对括号不能省略。函数子程序可以嵌套调用,即一个子程序可以调用另一个子程序,也可以递归调用,递归调用是指子程序直接或间接调用自己。【例7.1】 设计一个函数子程序GCD(M,N),求M,N的最大公约数,并调用函数子程序GCD(M,N)求75,35,215三个数的最大公约数。 program main integer gcd !说明函数子程序名是整型 print*,gcd(gcd(75,35),215) !嵌套调用函数子程序求3个数的最大公约数 end integer function gcd(m,n) !定义求2个数的最大公约数的函数子程序 k=min(m,n) do i=1,k if (mod(m,i)=0.and.mod(n,i)=0) gcd=i enddo end【例7.2】输入X,计算Y。其中sh是双曲正弦函数,已知: READ(*,*) XY=SH(1+SH(X)/(SH(2+X)+SH(3*X)WRITE(*,100) X, Y100 FORMAT(1X,X=,F6.2, Y=,F8.3)ENDFUNCTION SH(T)SH=(EXP(T)-EXP(-T)/2END4)函数子程序的特点 通过使用函数子程序,可以知道函数子程序特别适合计算类型的问题,对函数调用后期望得到一个结果数据的问题均可以方便地定义函数子程序。7.2.2 子例行程序函数子程序可以很好的完成数值计算功能,但在实际应用中有很多的功能模块只要求执行某种操作而不需要带出返回值。此时就可以采用FORTRAN提供的另一种模块化程序结构子例行程序。 1)子例行程序定义的一般形式:SUBROUTINE 函数名(形参表)END子例行程序的定义与函数子程序的定义类似,要注意的是这二者的区别:(1)子例行程序的名称不用来返回函数的处理结果,因此是没有类型的。所以既不能定义子例行程序名的类型,也不能在子例行程序中给子例行程序名赋值;(2)子例行程序必须以SUBROUTINE语句开头,以END语句结尾。能用函数子程序实现的功能,都能用子例行程序来实现,反之亦然。因此在程序设计时,要对具体的问题进行具体分析,以便确定选用哪种方式。一般来说:如果要得到一个函数值,并且该函数值在调用程序中还要参与运算,则使用函数子程序比较合适;如果不需要返回值或需要返回多个值,则使用子例行程序比较合适。但要注意的是由于不能给子例行程序名赋值,所以在将函数子程序转换为子例行程序时,应该增加一个变量用来带回在函数子程序中由函数名带出的子程序处理结果。2)子例行程序的调用子例行程序的调用也是采用实参代替形参的方式。但与函数子程序的引用方法不同的是,子例行程序的调用需要一个专门的语句。其基本形式:CALL 子程序名(形参名)当定义的子例行程序没有参数时,调用时可以不带上括号。这一点与函数子程序不同。将【例7.1】改写成子例行子程序:program main integer gys1,gys2 call gcd(75,35,gys1) !调用子例行程序得到2个数的最大公约数保存在gys1中 call gcd(gys1,215,gys2) !调用子例行程序求3个数的最大公约数 print*,gys2 end subroutine gcd(m,n,gys) integer gys k=min(m,n) do i=1,k if (mod(m,i)=0.and.mod(n,i)=0) gys=i enddo end将【例7.2】改写成子例行程序来实现。READ(*,*) XCALL SH(X,Y1)CALL SH(1+Y1,Y2)CALL SH(2*X,Y3)CALL SH(3*X,Y4)Y=Y2/(Y3+Y4)WRITE(*,100) X,Y100 FORMAT(1X,X=,F6.2, Y=,F8.3)ENDSUBROUTINE SH(T, Y)Y=(EXP(T)-EXP(-T)/2END7.2.3 数据块子程序数据块子程序是非执行程序单元,因而在其中不能出现任何可执行语句,也不能被别的任何程序调用。它是专门用来给有名公用区中的项目赋初值的子程序。具体内容将在7.3.3介绍。73 程序间的数据传递在调用子程序时,调用程序与被调用程序之间一般需要传递数据。数据传递有实参与虚参之间的虚实结合、通过COMMON语句实现数据的共享等方式。7.3.1 实参和虚参之间的数据传递虚实结合是常见的一种数据传递方式。在子程序被调用之前,所有形参既没有具体的存储地址,也没有具体的值,它实质上是实参的位置标志符,只是用来在形式上表示子程序中的自变量个数、类型及其在子程序中的处理规则。当子程序被调用时,实参就取代形参完成形参与实参的结合(简称为形实结合、虚实结合或参数传递),然后执行子程序。参数传递的方式如下:调用程序单元 CALL SUB(A, M1,TEST) 定义的子程序 SUBROUTINE SUB(X, N, CH )这种虚实结合与代数中函数的概念类似。形参是在定义子程序时出现在子程序名后面括号中的标识符,它可以是变量名、数组名(但不能是数组元素名)、子程序名等;而与其对应的实参则是在调用过程时传送给子程序的常量、变量名、数组名、数组元素、表达式、内部函数名、子程序名等。FORTRAN语言中的虚实结合一般采用按地址方式进行,即调用程序中的实参与子程序中的形参在子程序运行过程中共用同一个存储地址。特别值得注意的是:在子程序中,是按照形参的数据类型及格式去“理解”和使用对应实参内存单元中的数据,因而虚实结合遵循的是实参与形参的参数个数相同、按照位置一一对应进行传递,对应位置上数据类型相符的原则。1) 变量作为形参当形参是变量时,调用程序中对应的实参可以是同一类型的变量、常量、表达式或数组元素。当实参是变量或数组元素,则虚实结合时形参与实参使用同一个存储单元,因此在子程序中修改形参的值,也就相当于改变了调用程序中实参的值。有时也将这种参数传递方式称为双向传递。若对应的实参是常量或表达式,则虚实结合就只能理解为数据由调用程序传入子程序的单向传递过程,这时无论在子程序中如何修改形参的值也不会改变对应的实参的值。因此如果想要保证实参的值不变,可以把实参变量用一对圆括号扩起来,变为一个表达式;反之如果想要实现数据的双向传递,就应该在调用子程序之前将常量或表达式的值赋给变量,而在实参表出现相应的变量名。2) 数组作为形参不论数组的逻辑结构如何,在存储器中都被转换成一维结构来连续存放,因此作为形参或实参的数组之间都将按照该一维结构建立对应关系。当形参是数组名时,调用程序中对应的实参必须是同一类型的数组名或数组元素名,下面分别加以讨论:(1)实参为数组名当实参是数组名时,实参数组与形参数组按照地址结合,实参数组与形参数组都从第一个元素开始逐一对应,但形参数组与实参数组的结构和长度可以不同,只要满足实参数组的长度大于或等于形参数组的长度即可。例如,有如下的程序段:INTEGER A(2,3)CALL SUB1(A)ENDSUBROUTINE SUB1(B)INTEGER B(-1:3)END当在主程序中执行调用语句时,实参数组即与形参数组按如下关系结合: A(1,1) A(2,1) A(1,2) A(2,2) A(1,3) A(2,3)B(-1) B(0) B(1) B(2) B(3)其中,实参是一个二维数组,有6个整型元素,第一个元素为A(1,1),而形参是一个一维整型数组,其第一个元素为B(-1)。由此可以看到:用数组名作实参时,都从第一个元素开始进行地址匹配,后面的元素逐个对应使用相应的存储单元。(2)实参为数组元素与数组名作为实参类似,当实参是数组元素时,实参数组与形参数组也是按照地址来结合的。此时形参数组的元素与作为实参的给定数组元素及其之后的数组元素进行逐个对应。此时也不要求形参数组与实参数组的结构和长度相同,只要满足从作为实参的数组元素开始到数组结尾的实参的个数大于或等于形参数组所定义出的形参的个数即可。例如,有如下的语句:INTEGER A(2,3)CALL SUB3(A(2,1)ENDSUBROUTINE SUB3(B)INTEGER B(-1:3)END当在主程序中执行调用语句时,实参即与形参按如下关系结合: A(1,1) A(2,1) A(1,2) A(2,2) A(1,3) A(2,3)B(-1) B(0) B(1) B(2) B(3)在此例中,实参是二维整型数组中的一个元素,而形参是一个一维整型数组,则虚实结合就从形参的第一个元素B(-1)与作为实参的数组元素A(2,1)开始进行地址匹配,后面的元素逐个对应使用相应的存储单元。如果主程序中语句如下:INTEGER A(2,3)CALL SUB3(A(1,2)END则在主程序中执行CALL语句后,实参与形参的结合关系变为: A(1,1) A(2,1) A(1,2) A(2,2) A(1,3) A(2,3)B(-1) B(0) B(1) B(2) B(3)在这里虚实结合是从形参的第一个元素B(-1)与作为实参的数组元素A(1,2)开始进行地址匹配,后面的元素逐个对应使用相应的存储单元。结果形参数组元素B(3)没有对应的实参元素,虚实结合过程中就出现了错误。但这种错误很隐蔽,一般不容易发现。因此必须明确数组的存储结构,并且一定要保证实参中从作为实参的数组元素开始到数组结尾的元素个数大于或等于形参数组的元素个数。综合比较前面数组名与数组元素作为实参的情况,可以得出如下结论: 形参数组与实参数组的结构可以不同,但虚实结合时都是按照一维存储结构进行地址匹配。 当实参为数组名时,是从实参数组的第一个元素与形参数组的第一个元素开始依次对应结合;当实参为数组元素时,则是从该数组元素开始的实参数组元素与形参数组的所有元素逐个对应结合,而不仅仅是将作为实参的那一个数组元素传递给形参。从开始结合的实参数组元素到实参数组的最后一个元素之间的元素个数一定要大于或等于形参数组的元素个数。3) 可调数组作形参前面所用到的数组,其元素个数都是固定不变的。除此之外,在FORTRAN中允许在子程序中使用可调数组。可调数组是指其大小在子程序被调用之前并不确定,而是在调用过程中由传递过来的实参来确定。在主程序中定义数组时,数组下标的上下界必须是整型常量,而不能是变量或表达式。但在子程序中,允许使用整型变量来定义数组中各维下标的上下界,而整型变量的值应该通过参数传递由调用程序传入。这样即可在子程序中定义出可调数组。使用可调数组的优点是可以通过同一个子程序来处理不同结构的数组,从而大大提高子程序的通用性。例如,有如下的一段程序:INTEGER A(2,4)L=5CALL SUB5(A,L)N=8CALL SUB5(A,N)ENDSUBROUTINE SUB5(B,M)INTEGER B(M)END 子程序SUB5中的B是一个一维可调数组,数组元素个数(或称为数组的长度)由整型形参M给出,当主程序执行到调用语句时,形参M的值就由主程序的实参L或N传过来,从而确定可调数组的长度。其中的虚实结合关系如下:由L确定的虚实结合关系: 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)由N确定的虚实结合关系: 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) B(7) B(8)使用可调数组时除需要遵守数组使用的语法规定外,还必须注意以下要求: 可调数组名必须作为形参; 可调数组的每一维上下界必须是整型变量,而且必须在调用时能够通过参数传递获得确定的值; 使用可调数组时,一般要使传送给可调数组的各维下标的上下界与实参数组的一致。4)过程名作为形参 过程名(即函数子程序名或子例行程序名)也可以作为形参出现在形参表中,FORTRAN编译器完全能够根据某个形参在子程序中出现时的上下文关系来确定它是函数子程序名、子例行程序名或其他标识符的名称。因为若此形参在子程序中使用时,其后面跟有一对圆括号,而又没有相应的数组名定义,则此形参一定是函数子程序名;而若此形参在子程序中使用时,出现在子程序中的CALL语句中,则此形参一定是子例行程序名。在形参表中的子程序名,只是一个虚设的名称,并不代表程序中实际存在如此名称的一个函数子程序或子例行程序,实际的名称要在调用时通过实参向形参的传递来确定。形参是一个子程序名时,对应的实参应该是同一类型的子程序名。即当形参是函数子程序名时,对应的实参应该是函数子程序名或内部函数名,并且形参函数值与实参函数值应该具有相同的数据类型;当形参是子例行程序名时,对应的实参应该是子例行程序名。在调用程序的实参中如果出现外部函数名或子例行程序名时,必须在调用程序中用EXTERNAL语句来说明这些外部函数名或子例行程序名;在调用程序的实参中如果出现内部函数名,则必须在调用程序中用INTRINSIC语句来说明这些内部函数名。这里的EXTERNAL语句和INTRINSIC语句都是说明语句,用来说明本程序单位中作为实参的子程序名或内部函数名,而不是用来说明作为形参的子程序名。子程序名的虚实结合使FORTRAN程序可以方便地使用许多通用子程序(特别是内部函数),也使程序按照模块划分为独立的子程序分别编写、编译、调试成为可能,为软件的工程化组织和实施提供了技术上的保证。示例:INTRINSIC语句对内部函数的说明。INTRINSIC SIN, COSCALL SUB(SIN, 1.0, A)CALL SUB(COS, 2.0, B)WRITE(*, *)A, BWRITE(*,*)SIN(1.0), COS(2.0)ENDSUBROUTINE SUB(F, X, R)R=F(X)END【例7.4】 梯形法求积分根据例5.21中的积分方法,其计算公式为:=h=将梯形积分算法单独设计成一个子例行程序,被积函数单独设计成函数子程序,通过虚实结合来实现被积函数的传递。程序如下:FUNCTION FUNC(X)FUNC=1/(1+X)ENDSUBROUTINE TRINT(F,A,B,N,S)H=(B-A)/NS=H*(F(A)+F(B)/2.0DO 10 I=1,N-1S=S+H*F(A+I*H)10CONTINUEENDEXTERNAL FUNCREAD(*,*) NCALL TRINT(FUNC,0.0,1.0,N,R)WRITE(*,*) N,REND在此程序中采用的是指定积分区间的区间等分数N来求积分,实际上也可以不指定积分的区间等分数N,即“变步长”法:由程序自动地改变N的值,使N=2,4,6,8,直到满足指定的求积分精度。上面的子例行程序的五个形参中后四个是变量,第一个形参(F)是函数子程序名。因为在子例行程序中的表达式中出现了F(A)和(B),在FORTRAN中这样的形式要么是代表数组元素,要么代表函数调用。而在子例行程序中并没有说明F是数组,由此可以确定F是实型子程序名。因此在主程序的说明部分中将与形参F对应的实参“FUNC”用“EXTERNAL FUNC”语句加以说明。5)星号(*)作为形参 当形参是星号(*)时,与之对应的实参应该是一个冠有星号(*)的语句标号。例如:设计一个子例行程序,当参数C为加号(+)时,计算并打印A+B的值,C为减号()时,计算并打印A-B的值。program maincharacter*1 cread*,a,b,c !输入2个运算数和一个标志数call f(a,b,c,*10,*20,s) !调用子程序10 print*,a,+,b,=,s goto 3020 print*,a,-,b,=,s30 end subroutine f(a,b,c,*,*,s)character*1,cselect case(c) case (+) s=a+b return 1 !返回到第一个带有*号的语句(即10号) case (-) s=a-b return 2 !返回到第二个带有*号的语句(即20号)end select end6)变量的作用域FORTRAN为每个变量在内存中分配一个存储区域(根据类型决定字节数),一旦某变量完成了自己的使命,FORTRAN将收回该变量占据的存储区域,该变量将变成无定义。到底FORTRAN什么时候为变量分配存储区域,又什么时候收回存储区域呢?这就是变量的作用域问题。(1)变量存储区域的分配与收回 一个程序在投入运行时,机器将为该程序中的所有变量分配存储单元,当该程序退出运行时,机器将收回这个程序所占用的全部存储单元。(2)变量作用域 一个变量通常只在本程序单元中起作用,离开建立该变量的程序单元,变量就被收回,这种作用域的局限性,使用户在设计程序时,只需要考虑在本程序单元中的变量是否互相干扰,而无需要考虑与其他程序单元之间的变量干扰问题,简化了程序的调试工作。在程序调用时,调用程序单元的实参是通过与被调用程序单元的形参的结合来实现的,并不是调用程序单元中的变量直接在被调用程序单元中有定义。下面举例来说明变量作用域的问题。 subroutine f(a,b,c)integer x,yx=5 !子程序F中有X,Yy=8c=a+bprint*,x,y in sub,x,yendprogram mainu=5v=8x=3 !主程序MAIN中有X,Yy=2call f(u,v,s)print*,x,y in main,x,yprint*,u,v,send从程序执行结果可以看到,尽管在2个程序单元中都有X,Y,但它们是互不相干的变量,只是同名而已。(3)子程序中变量的存储属性 变量有数据类型,还有存储属性,数据类型决定变量的运算特征,而存储属性决定变量所占据的存储区域什么时候被收回。SAVE属性:当声明变量的子程序执行完毕后,具有本属性的变量依然保留,待下次调用该子程序时,这些变量的值延续使用。STATIC属性:具有该属性的变量在整个程序(不是声名该变量的子程序)执行中均有效AUTOMATIC属性:当声明该变量的子程序执行完毕后,具有该属性的变量被系统收回。这是变量的默认属性。下面举例说明变量存储属性的意义:program main do i=1,10 print*,i ,f() enddo end function f() static n,s !定义n,s为static属性 n=n+1 s=s+n f=s end如果将static n,s改为automatic n,s,结果是什么?请读者上机调试运行,并解释结果。7.3.2 数据共用存储单元和数据块子程序虚实结合是程序之间数据交换的一种方式,优点是:程序有较高的可读性,容易对数据的流向进行跟踪,便于程序的调试与维护。但采用虚实结合方式,数据传递的速度较慢,特别是多个程序单元之间有大量的数据要传递时速度问题就更为突出。FORTRAN语言还有另外一种数据交换方法共用存储单元。1) 等价语句从变量的概念可知,程序中的一个变量将被分配一个存储单元(该单元的字节数与数据类型有关),程序中对变量的使用即对该存储单元的访问。一般来说,不同的变量应该分配不同的存储单元,但是如果在程序中特别声明,将同一个程序单元中的2个或多个变量分配在同一个存储单元,这2个或多个变量就叫等价变量。声明等价变量的等价语句格式:Equivalence (变量表1),(变量表2),.例如:Equivalence(x,y,z) !定义x,y,z三个变量等价Equivalence(l,m,n),(a,b,c) !定义l,m,n等价, a,b,c等价Dimension (15),b(3,5)Equivalence(a,b) !定义数组a,b等价,按数组存储结构对应等价单元2) 公用区等价语句只能定义同一个程序单元中的不同变量共享相同存储单元。公用区就可以使不同程序单元中的变量共享相同存储单元。FORTRAN语言中的公用区分为有名公用区和无名公用区两种,任何一个程序中只能有一个无名公用区、多个有名公用区。程序中通过公用区说明语句(即COMMON语句)来开辟无名公用区或有名公用区。(1)公用区及其应用无名公用区COMMON 项目表或OMMON /项目表项目表中只允许出现变量、数组名或用于定义数组的数组说明符,多个项目之间要用逗号来分隔。使用无名公用区时,只要在两个程序单元中把要传递的变量放在无名公用区中的相同位置即可。例如,要在如下的子程序SUB1和SUB2中使用无名公用区,在变量X1与X2之间,在变量M1与M2之间分别进行数据传递的方式:SUBROUTINE SUB1COMMON X1,M1 ENDSUBROUTINE SUB2COMMON X2,M2 END或SUBROUTINE SUB1COMMON /X1,M1 ENDSUBROUTINE SUB2COMMON /X2,M2 ENDFORTRAN编译器在存储器中开辟一个连续的公用数据区,并且在其中依次安排在COMMON语句中出现的变量。因此在上面的子例行程序SUB1和SUB2中,两个COMMON语句中的变量依次对应公用区中的同一个内存单元。即实型变量X1与X2对应共享同一个存储单元,在两个子程序中,它们就对应具有相同的值。如果在SUB1子程序中修改了公用数据区中变量X1的值,也就同时改变了SUB2子程序中对应的变量X2的值,从而实现了数据的传递。反之也一样。而整型变量M1、M2的数据共享和传递情况与X1、X2的情况相同。COMMON语句的主要用途就是在不同的程序单元之间进行数据传递。例如主程序和子程序之间、多个子程序之间的数据传递;另外的一个用途是避免同一数据的反复多次存放,减少对存储空间的占用。使用建立无名公用区的COMMON语句需要注意以下几点要求: l COMMON语句是说明语句,因此它应该出现在相应程序单位中的所有可执行语句之前;l 一个程序在运行过程中虽然只能有一个无名公用区,但在同一个程序单元中,可以有多条无名公用区说明语句,这相当于把一个公用区说明语句分开来写。例如下面两条COMMON语句: COMMON A,B COMMON C,D就相当于: COMMON A,B,C,Dl 各程序单位中的公用区中的项目数可以不同。在进行数据传递时,是从公用区项目表中的第一个项目开始,依次逐一对应的。例如:SUBROUTINE SUB1COMMON X1,M1,N ENDSUBROUTINE SUB2COMMON X2,M2 END在无名公用区中的数据传递关系为:SUB1: X1 M1 NSUB2: X2 M2但此时,由于SUB1中变量N在SUB2中没有对应的变量,也就无法实现对变量N的数据传递。l 在同一个程序单元中,不论是变量还是数组,同一个名称最多只能在COMMON语句中出现一次。例如:SUBROUTINE SUB1COMMON X1,M1,X1 END其中的COMMON语句就有语法错误,可以改写为如下形式:SUBROUTINE SUB1COMMON X2,M2,Y1 END在不同的程序单位中的COMMON语句中的名称可以相同,但要注意的是数据传递不是按照名称来进行,而是按照位置对应关系来进行的。l 各程序单元中无名公用区中的变量或数组的类型必须按照其在公用区中的位置对应一致。例如,要将实型数组和整型数组放在同一公用区中,并分别实现在子程序之间正确的数据传递,可以写为:SUBROUTINE SUB3 COMMON A(5),M(2) ENDSUBROUTINE SUB4COMMON B(5),N(2) END由此例可以知道数组也可以通过COMMON语句加以说明。此时的数组说明也遵守前面介绍的数组说明规则,都应该能够把数组的名称、数组元素的类型以及数组的结构等内容确定下来。要注意的是此时是在定义数组,而不是在使用数组元素。数组元素的类型可以由隐含规则确定,也可以在COMMON语句之前显式说明。例如上面SUB3中的公用区可以如下定义:DIMENSION A(5), M(2)COMMON A,M或:REAL A INTEGER MCOMMON A(5),M(2)要注意的是,对于使用无名公用区语句定义的数组,不能用DATA语句赋初值。【示例】 给用无名公用区语句定义的数组赋初值。REAL AINTEGER M, YCOMMON A(5), M(3), Y(2)A=3DATA M/13, 23, 33/Y(1)=10Y(2)=100CALL SUB1ENDSUBROUTINE SUB1COMMON B(5), N(3), M(2) WRITE(*, *)B, N, MEND其中对数组M的赋初值会引起语法错误,而对数组A,Y的赋初值则是正确的。l 形参不能出现在公用区的项目表中。因为COMMON语句中的变量在编译时是要分配实际存储单元的,但形参在未被调用之前是不分配存储单元的。同样原因,可调数组也不能出现在COMMON语句的项目表中。有名公用区使用无名公用区,虽然可以实现多个程序单元之间大规模的数据高速传递。但是,在程序中只能有一个无名公用区,这时把不同元素类型、不同结构的数组与变量混合放在一块存储区中,影响对数据的使用,也很容易出错。为此FORTRAN语言提供了有名公用区,并且允许在程序中使用多个有名公用区,利用它们,可以很方便的实现数据的分类、分组传递。开辟有名公用区的COMMON语句的一般形式:COMMON /公用区名1/项目表1/公用区名2/项目表2 公用区名用来惟一标识一个有名公用区,其命名规则与变量的命名规则相同。公用区名可以与变量名相同,但不能与子程序名相同。项目表中允许出现变量、数组名或用于定义数组的数组说明符,多个项目之间应该用逗号来分隔,这一点与无名公用区中的情况相同。有名公用区中的数据只能在同名公用区中相同位置上的项目之间进行传递,即不同子程序中同名的有名公用区中的项目按位置关系对应一致。此外,使用有名公用区时还应注意:l 一个程序中只能有一个无名公用区,但可以有多个有名公用区,无名公用区与有名公用区可以在同一个COMMON语句中加以说明。【示例】 无名公用区与有名公用区混合说明。REAL ACHARACTER*20 YCOMMON A(5) /C1/M(3), Y(2) /K/B, K, DA=3DATA M/13, 23, 33/Y(1)=VISUAL FORTRANY(2)=TSHINGHUAK=2002CALL SUB1ENDSUBROUTINE SUB1CHARACTER*20 M(2)COMMON B(5) /C1/N(3), M /K/X, K, Z WRITE(*, *)B, NWRITE(*,*)M, X, K, ZEND其中定义了C1和K两个有名公用区(有名公用区K中又有一个变量K),可以看到对于使用有名公用区语句定义的数组,可以使用DATA语句赋初值,这与无名公用区中数组的赋初值是不同的。虽然可以将无名公用区与有名公用区放在同一个COMMON语句加以说明,但一般情况下,无名公用区与有名公用区是使用不同的COMMON语句来说明的,并且在使用有名公用区时,对于不同类型、不同结构的数据,将分别放在不同的有名公用区中,并且使用不同的COMMON语句来说明。这样便于阅读、调试和维护。l 同一有名公用区在各程序单元中的项目数一般应该相同。若项目数不同,则会在编译时出现相应的警告信息,但程序仍能正确运行。【示例】 同一有名公用区具有不同项目数。COMMON A(5), /C1/M(3), Y(2)A=3DATA M/13, 23, 33/Y(1)=10Y(2)=100CALL SUB1ENDSUBROUTINE SUB1COMMON B(5), /C1/N(2)WRITE(*, *)B, N ENDl 在一个程序单元的每个有名公用区中,不管是变量还是数组,同一个名称只能出现一次。例如:(1) COMMON A(5) /C1/M(3), Y(2), /C2/Y, B, C(2) COMMON A(5) /C1/M(3), Y(2), /C2/A, B, C(3) COMMON B(5), /C1/N(3)COMMON /C1/B(2)都是错误的,即是说在无名公用区与有名公用区之间、不同的有名公用区之间都不能有相同的名称出现。【例7.5】 利用公用区语句实现数据传递,计算前面引例所要求的组合数。COMMON K, WREAD(*,*)M, NDO 5 WHILE (M.LT.N)READ(*,*) M,N5 CONTINUEK=MCALL FACTS=WK=NCALL FACTS=S/WK=M-NCALL FACTS=S/W WRITE(*,*) S=,SENDSUBROUTINE FACTCOMMON N,TT=1.0DO 10 I=1,N10T=T*IEND3)数据块子程序数据块子程序是非执行程序单元,因而在其中不能出现任何可执行语句,也不能被别的任何程序调用。它是专门用来给有名公用区中的项目赋初值的子程序。数据块子程序的一般形式:BLOCK DATA 数据块子程序名程序体END数据块子程序名是用来惟一标识一个数据块子程序的,其命名规则与函数子程序、子例行程序的命名规则相同,但不能与程序中的变量、数组、公用区及子程序等的名称相同。数据块子程序可以没有名称,此时称为无名数据块子程序。在同一个程序中,可以有多个有名数据块子程序,但只能有一个无名数据块子程序。数据块子程序是一个完整的程序单元。它由BLOCK DATA语句开始,END语句结束。其程序体全由非执行语句构成,这些语句可以包括:类型说明语句、DIMENSION语句、COMMON语句、DATA语句、PARAMETER语句等。其中的COMMON语句与DATA语句是必须的,因为COMMON语句是用来实现与其他程序单位的联系的,而项目的赋值不能用赋值语句来实现,而是利用DATA语句实现的,并且最多也只能给项目赋值一次。数据块子程序只能对有名公用区中的变量或数组赋值,而不能给无名公用区中的变量或数组赋值。一个数据块子程序可以同时给多个有名公用区中的项目赋值,但是,一个有名公用区中的变量或数组却绝不能在不同的数据块子程序中赋值。如果只给部分项目赋值,COMMON语句中也必须完整的给出有名公用区中的所有项目。【示例】 数据块子程序对部分项目的赋值。BLOCK DATA D1CHARACTER*10 M(3)COMMON /C0/A(5), B /C1/MDATA A/5*3/, M/3* ABC111/ENDCALL SUB1ENDSUBROUTINE SUB1CHARACTER*10 M(3)COMMON /C0/A(5), B /C1/MWRITE(*, *)A, B, MEND综上所述,采用公用区,可以在不同的程序单元中对同一块存储区进行操作,共同使用其中的变量或数组元素。利用数据块子程序,在编译过程中对所需要的有名公用区中的项目赋初值,就相当于在程序执行过程中给多个程序单位中的变量或数组赋初值。这种数据传递方式,使得在多个程序单位中共享同一组数据成为可能。但是,使用公用区使得数据传递的走向不明确,同时也削弱了程序模块的相互独立性,给程序调试与维护带来不便。FORTRAN的子程序包括函数子程序、子例行程序与数据块子程序,前两种都是可执行程序单元,用来完成特定的功能,而数据块子程序是非执行程序单元,其功能是在程序编译阶段给有名公用区中的项目赋值,当程序开始运行时,它的任务就已经完成了。在三种子程序中实现程序模块之间的数据传递的方式有虚实结合、使用公用区两种。在程序中具体使用哪种方式,就需要根据具体情况来分析:对于传递的数据较多而且涉及到多个程序单元、多种传递方向,则可以考虑使用公用区的方法,这就相当于在内存中开辟了一个供多个程序单位共享的、存放大量数据的数据库;对于少量的数据传递,为了明确传递方向和减少程序之间数据的相互影响,则采用虚实结合的方式。也可以混合使用二者来实现数据的传递,以便更好的适应程序的结构要求与程序中的数据结构。74 模块化程序举例【例7.6】 将f和g分别定义成函数子程序,用子程序的嵌套调用求g(2.4, 3.7),g(4.6, 2.5)。已知:其中FUNCTION F(T)F=(1+EXP(-T)/(1-EXP(T)ENDFUNCTION G(X,Y)IF (X.LE.Y) THENG=F(X+Y)/(F(X)+F(Y)ELSEG=F(X-Y)/(F(X)-F(Y)ENDIFENDWRITE(*,*) G(2.4,3.7),G(4.6,2.5)END【例7.7】 验证歌德巴赫猜想:一个不小于6的偶数可以分解为两个素数之和。算法:对任一个偶数N,都有N=K1+K2,其中K1、K2都是整数,在此需要判定的是K1、K2是否同时都是素数。为此采取了枚举法一一试算,让K1从3开始,判断此时的K1、K2是否同时都为素数(先判断K1是否为素数。若是,再判断这时的K2又是否为素数),若不是,则让K1增加1,又判断这时的K1、K2是否都为素数。如此循环直到找到一组K1、K2即停止计算。LOGICAL FWRITE(*,*) IN
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年度房地产融资居间服务合同范本(专业版)
- 2025卜璧离婚协议书及婚后财产分割与子女抚养协议
- 2025年海上光伏产业技术创新与海洋能源产业技术创新产业竞争力提升
- 2025版砂石料生产设备维修与保养服务合同范本
- 2025版企业人力资源绩效评估与激励方案合同
- 2025年公共安全设施维护责任书
- 2025年度室内装饰装修材料生产与销售联盟合同
- 2025年度租赁房屋租赁纠纷处理与仲裁协议
- 2025版宠物个人买卖合同:宠物交易健康协议
- 2025版食品行业知识产权保护保密协议模板
- 输液反应-完整版
- 【高质量】如何进行有效的校本研修PPT文档
- 水泥生产企业生产安全事故综合应急预案
- 食堂安全培训-课件
- 胆总管结石伴急性胆管炎
- 制度编写书写规范
- 电缆购销合同文本参考
- 新员工质量保证考试(中软国际)
- 安徽涵丰科技有限公司年产6000吨磷酸酯阻燃剂DOPO、4800吨磷酸酯阻燃剂DOPO衍生品、12000吨副产品盐酸、38000吨聚合氯化铝、20000吨固化剂项目环境影响报告书
- 制造业业务流程
- 石英长石无氟浮选分离工艺研究现状
评论
0/150
提交评论