1、 第十三章 接口与模块 13.1 接口界面块在调用执行外部子程序时,一般要用 EXTERNAL 语句对调用的外部子程序进行声明,但这种声明只给出外部子程序名,并没有给出外部函数子程序名和参数的类型。由于主调程序单元与外部子程序独立编写,分别编译,所以仅声明子程序名有时系统难以获得外部子程序详细、准确的调用信息,从而无法正确编译,导致程序调用失败。FORTRAN 90 新增接口界面块功能可有效解决这些问题,同时还能提高程序的可读性。接口界面块可用在主程序单元、模块单元、外部子程序单元中,以指明主调程序与被调用外部子程序之间的接口信息,以便保证外部子程序的正确使用。例如,使用接口界面块求三个整数最
2、大值的示例程序如下:!主程序单元PROGRAM mainIMPLICIT NONEINTERFACEFUNCTION max3(a,b,c)IMPLICIT NONEINTEGER max3,a,b,cEND FUNCTIONEND INTERFACEINTEGER x,y,zREAD*,x,y,zWRITE(*,“(1X,三个数的最大值为 :,I4)“) max3(x,y,z)END!求三个整数最大值的外部函数子程序FUNCTION max3(a,b,c)INTEGER max3,a,b,c,maxmax=aIF (Bmax) max=BIF (Cmax) max=Cmax3=maxEND
3、FUNCTION131 接口界面块365接口界面块如同 EXTERNAL 语句一样,在一般情况下可缺省。对于一些常规函数和子例行程序,使用时不需要用 INTERFACE 接口声明它们的接口信息,但遇到以下情况必须在主调程序中使用接口界面块:外部函数返回结果是一个数组,即外部函数名类型为数组。外部函数返回结果是一个字符串,且长度不是常数,也不是假定长度(*)。外部函数返回结果是一个指针。外部子程序形式参数(哑元 )是一个数组片段。外部子程序实在参数是关键字变元或是缺省的可选变元。外部子程序扩展了赋值号的使用范围。外部子程序参数个数不确定。外部子程序改变参数传递位置。接口界面块的一般格式:INTE
4、RFACEEND INTERFACE FUNCTION ()END FUNCTIONSUBROUTINE ()END SUBROUTINE例如,定义一个接口界面块,声明了 2 个子例行程序接口界面和 2 个函数接口界面。INTERFACE!定义矩阵相乘子例行程序 P1 接口界面SUBROUTINE P1(A,B,C)INTEGER,DIMENSION(50,50) : A,B,CEND SUBROUTINE!定义矩阵转置子例行程序 P2 接口界面说明:接口界面块可出现在除 BLOCK DATA 数据块单元以外的其它三个程序单元(主程序单元、外部子程序单元、模块单元)中,放置在程序单元说明部分前
5、面。接口接体可以并列包含若干个函数或子例行程序接口界面定义。每个函数或子例行程序接口定义只给出子程序的 FUNCTION 语句、SUBROUTINE 语句、函数名与形式参数类型说明部分、END FUNCTION 语句、END SUBROUTINE 语句,不允许出现可执行语句。接口界面体中的函数名、子例行程序名、形式参数个数和形式参数类型必须与被调用的函数名、子例行程序名、形参个数和形参类型相同,但形参名称可以不同。接口界面体中不允许出现 ENTRY、DATA、FORMAT、语句函数语句。第十三章 接口与模块-366-SUBROUTINE P2(A1,A2)INTEGER,DIMENSION(4
6、0,50) : A1,A2END SUBROUTINE!定义矩阵相乘函数 F1 接口界面FUNCTION F1(X,Y)INTEGER,DIMENSION(50,50) : F1,X,YEND FUNCTION!定义计算向量 X 和 Y 内积的函数 F2 接口界面FUNCTION F2(X,Y)REAL F2,X(10),Y(10)END FUNCTIONEND INTERFACE接口界面块定义后,在调用其定义的外部子程序时可使用关键词指定实在参数,允许改变实在参数传递位置,如:CALL P1(matrix1,matrix2,matrix3) !按形参定义次序依次传递CALL P1(A=mat
7、rix1,C=matrix3,B=matrix2) !按指定形参关键字传递CALL P1(A=matrix1,C=matrix3) !B 数组内容区默认值13.2 模块外部子程序为人们提供了一种并行设计、共享使用子程序的有效机制,对提高程序设计质量和效率有很大帮助。外部子程序支持模块程序设计方法。外部子程序也有它的局限性,只能对子程序发挥作用,但对数据、类型或接口定义不能达到并行设计、共享使用的目的。目前,面向对象程序设计方法方兴未艾,支持面向对象程序设计方法,体现面向对象程序设计特色,已成为新一代程序设计语言不可缺少的内容。面向对象程序设计方法中有几个非常重要的概念:数据封装、特性继承、操作
8、重载、公私分隔等,这些概念的应用,使程序更加安全、可靠、高效,易于修改。例如,储蓄所保险柜中的钱款只有储蓄所有关人员才有权从中取钱或存钱,严格禁止顾客进行这些活动,顾客只能在柜台前完成存取款操作。如果储蓄所的保险柜对所有人开放其结果可想而知。储蓄所的操作规程是数据封装在现实生活中的具体应用。设计和编写程序亦如此。模块是 FORTRAN 90 新增功能,用以实现数据封装、特性继承、操作重载、公私分隔等面向对象特性,使程序安全、可靠、高效。模块中可声明常量、变量、数组、数据块、派生类型、接口界面块、模块函数、模块子例行程序,可看成是外部子程序功能的扩充。例如,使用模块实现数组有关操作的示例程序如下
9、:!定义模块 arrayMODULE array!声明私有名称,这些名称只能在本模块内使用。132 模块367PRIVATE max,n,score,init,sum !声明公有名称,这些名称可在本模块外其它程序单元中使用PUBLIC input,output,GetTotalNumber,average,OutputAverageScoreINTEGER,PARAMETER : max=50INTEGER : score(max),n=0CONTAINS!定义私有模块子例行程序 init,初始化成绩数组,即置实际人数 n 为 0SUBROUTINE init()n=0END SUBROUTI
10、NE!定义公有输入成绩数据模块子例行程序 input,同时统计实际人数 nSUBROUTINE input()INTEGER xWRITE(*,“(1X,请输入某班学生成绩 (整数),一行一个,最多 ,I2,个)“) maxPRINT*,输入-1 结束输入。READ*,xn=0DO WHILE (x/=-1)n=n+1score(n)=x; READ*,xENDDOEND SUBROUTINE input!定义公有输出成绩数据模块子例行程序 outputSUBROUTINE output()INTEGER iWRITE(*,“(1X,该班共有 ,I2,个学生)“) nPRINT*,学生成绩数据
11、依次为:WRITE(*,“(1X,(I3,:,)“) (score(i),i=1,n)END SUBROUTINE output!定义公有获得总人数数据模块子例行程序 GetTotalNumberSUBROUTINE GetTotalNumber(num)INTEGER numnum=nEND SUBROUTINE GetTotalNumber!定义私有求和模块函数 sumINTEGER FUNCTION sum() RESULT(t)INTEGER it=0第十三章 接口与模块-368-创建主程序单元文件 main.f90 和模块单元文件 array.f90,分别编译,一起构建,运行程序。提
12、示并输入数据:55,75,80,90,70,45,85,95,100,80,-1。输出结果为:该班共有 10 个学生学生成绩数据依次为:55, 75, 80, 90, 70, 45, 85, 95,100, 80总人数为:10平均成绩为:77.00该班学生平均成绩为:77.001. 模块定义模块定义一般格式:MODULE DO i=1,nt=t+score(i)ENDDOEND FUNCTION sum!定义公有求平均成绩模块函数 averageREAL FUNCTION average() Average=sum()/nEND FUNCTION average!定义公有输出平均成绩模块子例行
13、程序 OutputAverageScoreSUBROUTINE OutputAverageScore()WRITE(*,“(1X,该班学生平均成绩为 :,F5.2)“) average()END SUBROUTINE OutputAverageScoreEND MODULE array!主程序单元PROGRAM mainUSE arrayREAL avINTEGER numberCALL input !输入成绩数据CALL output !输出成绩数据CALL GetTotalNumber(number) !计算总人数,从实在参数变量 number 返回WRITE(*,“(1X,总人数为 :,
14、I2)“) numberav=average() !计算平均成绩WRITE(*,“(1X,平均成绩为 :,F5.2)“) avCALL OutputAverageScore !输出平均成绩END 132 模块369CONTAINSEND MODULE 例如,在计算体积和面积时,常常使用 值,可定义一个供其它有关程序单元使用的包含 值定义的模块。这样其它程序单元便可使用 pi 来作为 值。2. 共有和私有属性通过 PUBLIC 或 PRIVATE 语句,指定模块中数据对象和模块子程序的公有或私有属性。公有语句的一般格式是:PUBLIC 私有语句的一般格式是:PRIVATE 说明:在模块中首部可声
15、明常量(PARAMETER 语句)、变量、数组、数据块(COMMON 语句)、派生类型(TYPE 语句)、接口界面块(INTERFACE 块)、模块函数名、模块子程序名。这些被声明的对象可在本模块内使用,对于具有共有属性的对象也可在模块外其它程序单元中使用。在模块中可包含 CONTAINS 结构,允许定义模块函数和模块子例行程序。这些模块子程序可在模块内调用,对于具有共有属性的模块子程序也可在模块外其它程序单元中调用。在模块中可只有数据声明,或只有子程序定义,或两者都有。MODULE paiPARAMETER(pi=3.1415926)END MODULEPROGRAM mainUSE pai
16、READ*,rPRINT*,圆面积为:,pi*r*rEND说明:PUBLIC 后可为空,若为空,则指定模块中所有数据对象和子程序为公有,否则中给出的数据对象和子程序名具有公有属性。不为空的 PUBLIC 语句优先。说明:PRIVATE 后可为空,若为空,则指定模块中所有数据对象和子程序为私有,否则中给出的数据对象和子程序名具有私有属性。不为空的 PRIVATE 语句优先。第十三章 接口与模块-370-例如,下面给出使用公有和私有属性的模块。3. 模块使用要使用模块中具有公有属性的数据对象或子程序,必须在主调程序单元中使用 USE 语句进行声明。在模块中可通过 USE 语句使用其它模块的数据对象
17、或子程序。USE 语句的一般格式是:USE ,=例如,模块嵌套示例如下:MODULE maIMPLCIT NONEREAL score(50)INTEGER nEND MODULEMODULE mbUSE maREAL averageEND MODULESUBROUTINE FindAverage()USE mbINTEGER sumsum=0DO i=1,nSum=sum+score(i)MODULE mod1IMPLICIT NONEPRIVATE !指定所有变量 n1、n2、n3 为私有变量PUBLIC n3 !指定变量 n3 为公有变量,n1 和 n2 为私有变量INTEGER n1,
18、n2,n3END MODULE mod1MODULE mod2IMPLICIT NONEPUBLIC !指定所有变量 n1、n2、n3 为公有变量PRIVATE n3 !指定变量 n3 为私有变量,n1 和 n2 为公有变量INTEGER n1,n2,n3END MODULE mod2说明:模块定义和使用可嵌套。132 模块371ENDDOaverage=sum/nEND SUBROUTINE例如,模块换名示例如下:MODULE maIMPLCIT NONEREAL score(50)INTEGER nEND MODULEMODULE mbIMPLCIT NONEREAL score(50)I
19、NTEGER nEND MODULEPROGRAM mainUSE ma,score1=score,n1=nUSE mb,score2=score,n2=nEND例如,指定唯一属性示例如下:MODULE maIMPLCIT NONEREAL s1,s2,s3END MODULEMODULE mbIMPLCIT NONEREAL s1,s2END MODULEPROGRAM mainUSE ma,ONLY:s3USE mbs1=10;s2=20 !给模块 mb 中的 s1 和 s2 赋值s3=30 !给模块 ma 中的 s3 赋值END模块中名称可换名,即别名。当名称发生冲突时通过换名解决。模块
20、中名称可指定唯一属性(ONLY 属性)。第十三章 接口与模块-372-13.3 重载(OVERLOAD)INTERFACE 接口界面块不但能声明子程序的形参个数和类型,而且还能重载同名子程序、赋值号(=)和操作符(+、-、*、/、等),使 FORTRAN 90 具有了 C+语言的重载功能。所谓重载是指:一个子程序名允许具有若干不同功能,它们与若干个不同的子程序体相关联,调用时根据参数类型选择执行某一个子程序体。扩充赋值号和操作符功能,使其不但能对标准数据进行操作,而且也能对特定数据进行操作。在模块中使用 INTERFACE 块可实现重载功能。13.3.1 子程序重载程序中通常都要输出多种类型的
21、数据,不同类型数据的输出操作有所不同。如果不使用重载,则在程序中需要针对不同类型数据定义不同的子程序来实现这些输出操作。例如:(1) 针对整数,需要定义一个输出子程序 put_integer(i)(2) 针对实数,需要定义一个输出子程序 put_real(r)(3) 针对复数,需要定义一个输出子程序 put_complex(c)(4) 针对字符串,需要定义一个输出子程序 put_character(s)(5) 针对逻辑值,需要定义一个输出子程序 put_logical(l)(6) 针对数组,需要定义一个输出子程序 put_array(arr,n)使用时,需要熟悉每一个输出子程序名,给程序编写带
22、来诸多不便。使用重载功能可用一个输出子程序 put(x),来完成不同类型数据的输出,优点十分明显。例如,使用重载功能的模块示例如下:!定义模块单元MODULE outputIMPLICIT NONEINTERFACE putMODULE PROCEDURE put_integerMODULE PROCEDURE put_realMODULE PROCEDURE put_complexMODULE PROCEDURE put_characterMODULE PROCEDURE put_logicalMODULE PROCEDURE put_arrayEND INTERFACECONTAINSSU
23、BROUTINE put_integer(i)IMPLICIT NONEINTEGER,INTENT(IN) : iPRINT “(1X,i=,I4)“,iEND SUBROUTINE133 重载(OVERLOAD)373SUBROUTINE put_real(r)IMPLICIT NONEREAL,INTENT(IN) :rPRINT “(1X,r=,F7.2)“,rEND SUBROUTINESUBROUTINE put_complex(c)IMPLICIT NONECOMPLEX,INTENT(IN) : cPRINT “(1X,c=,F7.2,+,F7.2,i)“,REAL(c),AI
24、MAG(c)END SUBROUTINESUBROUTINE put_character(s)IMPLICIT NONECHARACTER(*),INTENT(IN) : sPRINT “(1X,str=,A)“,sEND SUBROUTINESUBROUTINE put_logical(l)IMPLICIT NONELOGICAL,INTENT(IN) : lPRINT “(1X,log=,L1)“,lEND SUBROUTINESUBROUTINE put_array(arr,n)IMPLICIT NONEINTEGER n,iINTEGER,INTENT(IN) : arr(n)PRINT “(1X,arr=,(I3,:,)“,(arr(i),i=1,n)END SUBROUTINEEND MODULE output!定义主程序单元PROGRAM mainUSE outputINTEGER : A(10)=(/10,11,12,13,14,15,16,17,18,19/),n=10CALL put(125)CALL put(12.5)CALL put(12.5,25.5)CALL put(苏州科技大学)CALL put(.TRUE.)CALL put(A,n)END