Fortran/記憶體管理
在 Fortran90 標準之前的大多數 Fortran 程式使用自包含資料,沒有結構,也沒有太多共享的結構化資料。但是,可以使用公共塊以結構化和非結構化的方式共享資料。此外,Fortran 程式中幾乎沒有進行記憶體管理。在 Fortran90 之前,分配的儲存甚至不可能,除了透過某些擴充套件(例如 Cray 指標)。然而,現代 Fortran 支援許多現代程式設計正規化,完全支援可分配資料(包括可分配型別),並允許使用指標。
自從 Fortran90 以來,共享變數透過使用模組得到了方便的管理。在 Fortran90 標準之前,公共塊用於定義全域性記憶體;在現代 Fortran 中,不建議使用它們。Fortran 模組還可以包含子程式和函式,但我們將把這些功能的討論留待以後。至於共享變數的管理,它們可以在模組中定義
module shared_variables
implicit none
private
integer, public, save :: shared_integer
integer, public, save :: another_shared_integer
type, public :: shared_type
logical :: my_logical
character :: my_character
end type shared_type
type (shared_type), public :: shared_stuff
end module shared_variables
請注意,即使模組只包含公共變數,也建議將其宣告為私有。雖然 save 是模組中變數的預設值,這意味著它會在模組中的變數使用時保留其先前值,但有時建議明確地進行此操作。然後可以在主程式中使用該模組
program my_example
use shared_variables, only: shared_integer, shared_stuff
implicit none
integer :: some_local_integer
! This will work and assign shared_integer to some local variable.
shared_integer = some_local_integer
! This will print the component my_character from type shared_stuff
! to stdout.
write (*,*) shared_stuff%my_character
! This, however, will not work, since another_shared_integer was not
! imported from the module - the program will not compile.
shared_integer = another_shared_integer
end program my_example
在現代 Fortran 標準(Fortran90 及更高版本)中,公共塊已被模組中公共變數的使用所取代。但是,由於它們在較舊的 Fortran 標準(77 及更早版本)中的使用,它們在歷史上很重要。公共塊是 Fortran 在 Fortran90 之前的標準中使用共享的公共儲存的方式。在最簡單的形式中,公共塊是一種定義全域性記憶體的方式。但是要小心。在大多數語言中,公共記憶體中的每個專案都是作為單獨的全域性已知名稱共享的。然而,在 Fortran 中,公共塊是一個共享的東西。我將展示幾個示例,但每個示例都將共享 i 和 another_integer 以及 my_array,一個 10x10 的實數陣列。
例如,在 C 中,我可以使用以下方法定義共享記憶體
int i;
int another_integer;
float my_array[10][10];
並在其他地方使用這些資料
extern float my_array[10][10];
extern int i;
extern int another_integer;
請注意,一個模組宣告儲存,另一個模組使用儲存。另請注意,定義和用法順序不同。這是因為在 C 中,就像在大多數語言中一樣,i、another_integer 和 my_array 都是共享專案。在 Fortran 中並非如此。在 Fortran 中,所有共享此儲存的例程都將具有類似於此的定義
common i, another_integer, my_array
integer another_integer
real my_array(10,10)
此公共塊儲存為資料塊,作為可連結的命名結構。唯一的問題是我們不知道它的名字。各種編譯器會給這個塊起各種名字。在某些系統中,該塊實際上沒有名稱。我們可以透過給結構體起一個名字來避免這個問題。例如,
common /my_block/ i, another_integer, my_array
integer another_integer
real my_array(10,10)
使用此形式,兩個不同的 Fortran 程式可以識別相同的儲存區域並共享它,而無需知道所有共享儲存的結構。同樣使用這種格式,C 或其他程式可以共享儲存。例如,希望共享此儲存的 C 程式將宣告相同的儲存,如下所示
extern struct {
int i;
int another_integer;
float my_array[10][10];
} my_block;
在上面的示例中,my_block 名稱匹配至關重要,以及型別、大小和順序匹配。但是,由於這些名稱僅在本地已知,因此內部名稱不必匹配。另請注意,在上面的示例中,Fortran 的 my_array(i,j) 與 C 的 my_block.my_aArray[j][i] 相匹配。
內在資料型別的位元組對齊可以透過簡單地使用適當的種類來確保。Fortran 沒有任何方法可以自動確保派生資料型別是位元組對齊的。但是,程式設計師可以很容易地確保插入適當的資料填充。例如,假設我們有一個派生型別,它包含一個字元和一個整數
type :: my_type
integer (kind=4) :: ival
character (len=1) :: letter
end type
此型別的陣列將具有大小為 5 位元組的元素。如果我們希望此型別陣列的元素每 8 個位元組對齊一次,我們需要新增 3 個位元組的填充。我們可以透過新增僅作為填充的字元來做到這一點。
type :: my_type
integer (kind=4) :: ival
character (len=1) :: letter
character (len=3) :: padding
end type
在 Fortran 中,可以使用 指標 作為其他資料的某種 別名,例如矩陣中的一行。
每個指標都處於以下狀態之一
- 未定義:如果指標沒有被初始化,則在定義後立即處於此狀態
- 已定義
- 空/未關聯:不是任何資料的別名
- 已關聯:某些資料的別名。
內在函式 associated 區分第二種和第三種狀態。
我們將使用以下示例:令指標ptr為某個實際值x的別名。
real, target :: x
real, pointer :: ptr
ptr => x
在下一個示例中,我們將使用一個實際矩陣matr作為目標,指標ptr應該成為特定行的別名。
real, dimension (4, 4), target :: matr
real, dimension (:), pointer :: ptr
ptr => matr(2, :)
指標也可以指向其他指標。這會導致它們成為第一個指標所指向的相同資料的別名。請參見下面的示例。
real, target :: x
real, pointer :: ptr1, ptr2
ptr1 => x
ptr2 => ptr1
指標的普通賦值和指標賦值之間的區別可以用以下等式來解釋。假設以下設定
real, target :: x1, x2
real, pointer :: ptr1, ptr2
ptr1 => x1
ptr2 => x2
指標的普通賦值會導致它們指向的資料的賦值。可以透過以下兩個等效語句來觀察這一點。
! Two equal statements
ptr1 = ptr2
x1 = x2
相反,指標賦值會改變其中一個指標的別名,而不會改變底層資料。請參見以下等效示例語句。
! Two equal statements
ptr1 => ptr2
ptr1 => x2
在定義指標後,可以使用allocate命令為其分配記憶體。指標指向的記憶體可以透過deallocate命令釋放。請參見以下示例。
program main
implicit none
real, allocatable :: ptr
allocate (ptr)
ptr = 1.
print *, ptr
deallocate (ptr)
end program main
您可以宣告一個數組具有已知的維度數量,但使用分配可以使其大小未知
real, dimension (:,:), allocatable :: my_array
allocate (my_array(10,10))
deallocate (my_array)
您也可以宣告一個指標
real, dimension (:,:), pointer :: some_pointer
allocate (some_pointer(10,10))
deallocate (some_pointer)
在古老版本的 FORTRAN(77 及更早版本)中,您只需要一個大的靜態陣列,然後使用其中您需要的部分。