【Fortran】过程设计之一(子例程SUBROUTINE)
目錄
- 前言
- Fortran子例程(SUBROUTINE)
- 1) 使用方式
- 2) 子例程示意
- 3) INTENT屬性
- 4) 傳遞數(shù)組給子例程
- 4) 傳遞可分配數(shù)組給子例程
- 5) 傳遞字符變量給子例程
- 6) 子例程作為參數(shù)傳遞
- 7) 其它注意事項
前言
程序單元包括主程序、子例程、模塊、函數(shù)子程序。
在Fortran中,大型程序可以拆分成多個獨立運行和調(diào)試的子任務(wù),即程序單元(亦稱為外部過程)。
Fortran中有兩種外部過程:子例程和函數(shù)子程序。這種機制的優(yōu)點是:
- 子任務(wù)單獨測試,相互之間不影響;
- 避免重復(fù)造輪子,調(diào)用即可;
- 將實現(xiàn)某一功能的代碼封裝起來,避免不經(jīng)意修改導(dǎo)致代碼錯誤而不自知。
Fortran子例程(SUBROUTINE)
1) 使用方式
子例程(亦可稱為子程序)是一個Fortran過程,通過CALL語句進行調(diào)用,并通過參數(shù)表獲取輸入數(shù)值和返回結(jié)果。
定義語句格式:
注意事項:
- subroutine_name由字母、數(shù)字和下劃線組成 ,最大長度可達(dá)63個字符,第一個字符為字母;
- argument_list_dum,形參,一系列變量和/或數(shù)組,從調(diào)用程序傳遞給子例程;
- 子例程是一個獨立的程序單元,開始于SUBROUTINE,結(jié)束于END SUBROUTINE,其中的局部變量名和語句標(biāo)號(“行號”)可以在其它地方復(fù)用(不用擔(dān)心重名);
- 實際上沒有給形參分配內(nèi)存。
調(diào)用語句格式:
CALL subroutine_name( argument_list_act )
注意事項:
- 任何可執(zhí)行程序單元都可以調(diào)用子例程,但不能調(diào)用自身(除非定義為遞歸類型);
- argument_list_act,實參。實參的個數(shù)、順序與類型必須和形參的個數(shù)、順序與類型相匹配。
- 主程序和子例程之間采用地址傳遞進行參數(shù)傳遞,具體過程是:由于主程序中的實參有具體的內(nèi)存存儲位置,當(dāng)調(diào)用子例程時,主程序?qū)⑸啥鄠€指針來指向各個實參所對應(yīng)的存儲位置,并將指針傳遞給子例程,子例程調(diào)用的是參數(shù)的內(nèi)存位置,而非實參數(shù)據(jù)本身。
2) 子例程示意
已知三角形的兩條直邊,計算斜邊。要求計算過程用子例程,主程序直接輸入相應(yīng)數(shù)據(jù)后直接調(diào)用。
PROGRAM calc_hypotenuse_test ! 主程序 IMPLICIT NONE REAL :: s1 REAL :: s2 REAL :: hypotWRITE(*,*) '測試計算斜邊的子例程' WRITE(*,*)'輸入第一條直邊的長度:' READ(*,*) s1 WRITE(*,*)'輸入第二條直邊的長度:' READ(*,*) s2CALL calc_hypotenuse( s1, s2 , hypot ) ! 調(diào)用子例程WRITE( *, 100 ) hypot 100 FORMAT('斜邊長度為:' , F10.4)STOP END PROGRAM calc_hypotenuse_test SUBROUTINE calc_hypotenuse(side_1 , side_2 , hypotenuse) ! 子例程 IMPLICIT NONE REAL , INTENT(IN)::side_1 ! 第一條直邊長度,輸入,INTENT用法見下述 REAL , INTENT(IN)::side_2 ! 第二條直邊長度,輸入 REAL , INTENT(OUT)::hypotenuse ! 斜邊長度,輸出REAL::temp ! 聲明局部變量 temp = side_1**2 + side_2**2 hypotenuse = SQRT( temp ) END SUBROUTINE calc_hypotenuse3) INTENT屬性
INTENT屬性在子例程的形參聲明時使用。
INTENT屬性的格式如下:
- INTENT(IN),形參僅用于向子程序傳遞輸入數(shù)據(jù);
- INTENT(OUT),形參僅用于將結(jié)果返回給調(diào)用程序;
- INTENT(INOUT) 或 INTENT(IN OUT),形參既用來向子程序輸入數(shù)據(jù),也用來向調(diào)用程序返回結(jié)果。
屬性特點:
- 對于每一個形參來說,都應(yīng)該聲明一個合適的INTENT屬性;
- INTENT屬性僅對過程的形參有效,如果用來聲明子例程的局部變量或主程序的變量則會出錯;
- 對于每一個過程,都應(yīng)該聲明每一個形參的INTENT屬性。形參的INTENT屬性也可以用獨立的語句來聲明,如:INTENT(IN) :: arg1 , arg2,...。
4) 傳遞數(shù)組給子例程
如前所述,調(diào)用參數(shù)實際上是通過傳遞指向該實參的內(nèi)存位置指針來傳遞給子例程。對于實參是一個數(shù)組,其指針是指向數(shù)組中的第一個值。然而,子例程需要同時知道數(shù)組的地址和大小,保證不會發(fā)生越界,才能進行數(shù)組操作。
在子例程中有三種方式來指明形參數(shù)組的大小:
顯式結(jié)構(gòu)形參數(shù)組
數(shù)組的維度大小需要作為參數(shù)進行傳遞,一維數(shù)組為例:
二維數(shù)組為例:
SUBROUTINE process1(data1 , data2 , m , n) INTEGER , INTENT(IN) :: m ,n ! m×n為數(shù)組的大小 REAL , INTENT(IN) , DIMENSION(m,n)::data1 REAL , INTENT(OUT) , DIMENSION(m,n)::data2 data2 = 3.*data1 ! 將data1數(shù)組的數(shù)值乘以3,賦值為data2數(shù)組,直接對數(shù)組進行操作END SUBROUTINE process1由于形參數(shù)組的大小和結(jié)構(gòu)都已經(jīng)清晰,可以對形參數(shù)組進行數(shù)組操作,以及切片操作。
不定結(jié)構(gòu)形參數(shù)組
把子例程中的所有形參數(shù)組聲明為不定結(jié)構(gòu)(數(shù)組的每個下標(biāo)用:來代替)的形參數(shù)組,只有當(dāng)子例程具有顯式接口時,才能使用這種數(shù)組。因此,顯示接口能夠給編譯器提供每個數(shù)組的大小、結(jié)構(gòu)等詳細(xì)信息,在調(diào)用的時候不會出錯。
需注意的是,定義形參數(shù)組時只有它的結(jié)構(gòu)(但用:替代),沒有具體下標(biāo)范圍。因此,在把實參數(shù)組傳遞至形參數(shù)組時,只傳遞了結(jié)構(gòu),并沒有傳遞實參數(shù)組每個維度的下標(biāo)取值范圍。此時,可以用查詢函數(shù)獲取不定結(jié)構(gòu)數(shù)組的結(jié)構(gòu)。
如果不需要將數(shù)組的每個維度下標(biāo)邊界從調(diào)用程序傳遞給子例程,則不定結(jié)構(gòu)形參數(shù)組比顯式結(jié)構(gòu)形參數(shù)組更方便使用。 二維數(shù)組的例子:
MODULE module_process CONTAINSSUBROUTINE process2(data1 , data2 )REAL , INTENT(IN) , DIMENSION(:,:)::data1 REAL , INTENT(OUT) , DIMENSION(:,:)::data2 data2 = 3.*data1 ! 將data1數(shù)組的數(shù)值乘以3,賦值為data2數(shù)組,直接對數(shù)組進行操作END SUBROUTINE process2 END MODULE module_process不定大小形參數(shù)組
古老且過時的方法,用星號*來聲明形參數(shù)組的長度,表示大小不確定。因此不清楚數(shù)組的實際大小和結(jié)構(gòu),容易運行錯誤,且很難調(diào)試,建議不要使用。例子如下:
4) 傳遞可分配數(shù)組給子例程
可分配數(shù)組作為參數(shù)傳遞給子例程時,必須要結(jié)合顯式接口。
注意事項:
-
當(dāng)傳遞的參數(shù)是可分配數(shù)組時,子例程中形參聲明 和 調(diào)用子程序的實參聲明必須都是可分配的;
-
可分配形參可以使用INTENT屬性,但INTENT屬性的具體參數(shù)可能會影響子例程中的操作:
- INTENT(IN),在子例程中不允許對輸入可分配數(shù)組進行重分配或者釋放內(nèi)存空間;
- INTENT(INOUT),調(diào)用子例程時,如果參數(shù)只有數(shù)組這一個,那么實際上會將數(shù)組的狀態(tài)(是否可分配)和相應(yīng)數(shù)據(jù)傳遞至子例程中(實參——>形參),在子例程中可以對形參修改、釋放內(nèi)存、重分配等操作。形參的最終狀態(tài)和數(shù)據(jù)返回至調(diào)用程序中(相應(yīng)實參結(jié)果被修改了)。具體見下例;
- INTENT(OUT),調(diào)用子例程時,實參在入口處被自動釋放掉,相應(yīng)的數(shù)據(jù)清除掉,可以在子例程中對其進行操作,然后將形參的最終狀態(tài)和數(shù)據(jù)返回至調(diào)用程序中。
例子:
取自《Fortran for Scientists and Engineers(4th) by Stephen J. Chapman》中的9-5例題,有少量修改,復(fù)制可運行。
相應(yīng)的結(jié)果為:
調(diào)用子程序前主程序中的數(shù)組: 1.0 2.0 3.0 4.0 5.0 6.0 子程序分配成功! 子程序輸入為: 1.0 2.0 3.0 4.0 5.0 6.0 子程序中輸出的數(shù)組 5.0 4.0 3.0 2.0 1.0 調(diào)用子程序后主程序中的數(shù)組: 5.0 4.0 3.0 2.0 1.05) 傳遞字符變量給子例程
當(dāng)一個字符變量被作為子例程的形參時,用*號來聲明字符變量的長度。當(dāng)調(diào)用子例程時,形參的長度將是實參的長度。如:
SUBROUTINE example( string ) CHARACTER( len = * ) , INTENT(IN) :: string ! *號表示完全復(fù)制實參的長度 WRITE(*,*) 'The lengrh of string : ',LEN(string) ! 可以在子例程內(nèi)部實時返回實參的長度 END SUBROUTINE example如自動數(shù)組一般,創(chuàng)建自動字符變量:
SUBROUTINE sample ( string ) CHARACTER(len=*) :: string CHARACTER(len=len(string)) :: temp ! 一個與形參相同大小的臨時變量,子例程調(diào)用結(jié)束時會被銷毀6) 子例程作為參數(shù)傳遞
當(dāng)子例程a作為實參時,傳遞給過程A(如另一子例程)一個指向該子例程a的指針。執(zhí)行該過程A時,參數(shù)表中的子例程a將作為形參進入到過程A的編譯當(dāng)中。
要想實現(xiàn)子例程傳遞功能,必須要使用EXTERNAL屬性,將子例程聲明為外部,此時編譯器才會知道參數(shù)表中傳遞的是獨立的已編譯子例程,而不是常規(guī)變量。
EXTERNAL屬性需要在聲明部分中使用,格式如下:
TYPE, EXTERNAL ::sub_1 , sub_2 ! TYPE是指具體數(shù)據(jù)類型或者
EXTERNAL ::sub_1 , sub_2同時,在過程中需要結(jié)合CALL語句,以調(diào)用過程中子例程形參。
7) 其它注意事項
- 不要在子例程中使用STOP語句,因為一旦調(diào)用子例程時,會停止程序。如果調(diào)用多個子例程(每個子例程都有STOP語句),則程序永遠(yuǎn)不會執(zhí)行成功;
- 如果子例程中存在可能引發(fā)錯誤的條件,應(yīng)該對錯誤進行檢測,并設(shè)置正確/錯誤標(biāo)志,返回給調(diào)用程序(即作為形參其中之一),用于實時判斷預(yù)設(shè)條件的執(zhí)行成功與否(即可定義為一種狀態(tài)用于判斷)。
總結(jié)
以上是生活随笔為你收集整理的【Fortran】过程设计之一(子例程SUBROUTINE)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 边缘计算网关 5G/4G物联网工业互联
- 下一篇: dya61