跳轉到內容

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 中,公共塊是一個共享的東西。我將展示幾個示例,但每個示例都將共享 ianother_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 中,就像在大多數語言中一樣,ianother_integermy_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 及更早版本)中,您只需要一個大的靜態陣列,然後使用其中您需要的部分。

華夏公益教科書