Dichiarazione Fortran SAVE

Ho letto della dichiarazione di save nel documento di riferimento della lingua (Intel), ma non riesco a capire cosa faccia. Qualcuno potrebbe spiegarmi in un linguaggio semplice cosa significa quando l’istruzione save è inclusa in un modulo?

In linea di massima quando un modulo diventa fuori campo, le variabili di quel modulo diventano indefinite, a meno che non siano dichiarate con l’attributo SAVE, o venga usata un’istruzione SAVE. “Non definito” significa che non ti è permesso fare affidamento sulla variabile che ha il valore precedente se usi di nuovo il modulo – potrebbe avere il valore precedente quando riaccedi al modulo, o potrebbe non farlo – non c’è garanzia . Ma molti compilatori non lo fanno per le variabili del modulo – le variabili probabilmente mantengono i loro valori – non vale la pena per il compilatore di capire se un modulo rimane nell’ambito o meno e probabilmente le variabili del modulo sono considerate globali variabili – ma non fare affidamento su questo! Per sicurezza, usa “salva” o “usa” il modulo dal programma principale in modo che non diventi mai fuori portata.

“save” è importante anche nelle procedure, per memorizzare lo “stato” attraverso le invocazioni della subroutine o della funzione (come scritto da @ire_and_curses) – inizializzazioni, contatori, ecc. “prima invocazione”

 subroutine my_sub (y) integer :: var integer, save :: counter = 0 logical, save :: FirstCall = .TRUE. counter = counter + 1 write (*, *) counter if (FirstCall) then FirstCall = .FALSE. .... end if var = .... 

eccetera.

In questo frammento di codice, “counter” riporterà il numero di invocazioni della subroutine x. Sebbene in realtà in Fortran> = 90 si può omettere il “salvataggio” perché l’inizializzazione nella dichiarazione implica “salva”.

Contrariamente al caso del modulo, con i compilatori moderni, senza l’attributo save o l’inizializzazione su una dichiarazione, è normale che le variabili locali delle procedure perdano i loro valori attraverso le invocazioni. Pertanto, se si tenta di utilizzare “var” in una chiamata successiva prima di ridefinirla in tale chiamata, il valore non è definito e probabilmente non sarà il valore calcolato su una precedente chiamata della procedura.

Ciò è diverso dal comportamento di molti compilatori FORTRAN 77, alcuni dei quali mantenevano i valori di tutte le variabili locali, anche se ciò non era richiesto dallo standard di lingua. Alcuni vecchi programmi sono stati scritti facendo affidamento su questo comportamento non standard – questi programmi non funzioneranno con i nuovi compilatori. Molti compilatori hanno un’opzione per usare il comportamento non standard e “salvare” tutte le variabili locali.

LATER EDIT: aggiornamento con un esempio di codice che mostra l’uso non corretto di una variabile locale che dovrebbe avere l’attributo save ma non lo fa:

 module subs contains subroutine asub (i, control) implicit none integer, intent (in) :: i logical, intent (in) :: control integer, save :: j = 0 integer :: k j = j + i if ( control ) k = 0 k = k + i write (*, *) 'i, j, k=', i, j, k end subroutine asub end module subs program test_saves use subs implicit none call asub ( 3, .TRUE. ) call asub ( 4, .FALSE. ) end program test_saves 

La variabile locale k della subroutine viene intenzionalmente utilizzata in modo errato – in questo programma viene inizializzata nella prima chiamata poiché il controllo è TRUE, ma sul controllo della seconda chiamata è FALSE, quindi k non viene ridefinito. Ma senza l’attributo save k non è definito, quindi l’utilizzo del suo valore è illegale.

Compilando il programma con gfortran, ho trovato che k conservava comunque il suo valore:

  i, j, k= 3 3 3 i, j, k= 4 7 7 

Compilando il programma con ifort e opzioni di ottimizzazione aggressive, k ha perso il suo valore:

  i, j, k= 3 3 3 i, j, k= 4 7 4 

Usando ifort con le opzioni di debug, i problemi sono stati rilevati in fase di esecuzione!

  i, j, k= 3 3 3 forrtl: severe (193): Run-Time Check Failure. The variable 'subs_mp_asub_$K' is being used without being defined 

Pubblicare questo come una risposta a MSB perché la mancanza di formattazione nei commenti probabilmente farebbe la colazione di un maiale fuori dal tutto:

Innanzitutto, grazie per aver risposto, entrambi. Lo apprezzo.

Se ho capito bene;

  subroutine save(j) implicit none integer :: i = 0, j save i i = i + j write(*,*)i end subroutine save program test_save implicit none integer :: j j = 1 call save(j) call save(j) end program test_save 

Se non fosse per l’istruzione SAVE nel precedente esempio, la variabile i (il valore della variabile) verrebbe “persa” dopo la prima chiamata della subroutine di salvataggio. Grazie ad esso, mantiene il suo valore – “1” in questo caso, e per questo aumenta a “2” durante la seconda chiamata.

Ho capito bene? Vicino forse?

Normalmente, le variabili locali escono dall’ambito di applicazione una volta che l’esecuzione lascia la procedura corrente e quindi non hanno alcuna ‘memoria’ del loro valore su invocazioni precedenti. SAVE è un modo per specificare che una variabile in una procedura deve mantenere il suo valore da una chiamata alla successiva. È utile quando si desidera memorizzare lo stato in una procedura, ad esempio per mantenere un totale parziale o mantenere la configurazione di una variabile.

C’è una buona spiegazione qui , con un esempio.

Una breve spiegazione potrebbe essere: l’attributo save dice che il valore di una variabile deve essere preservato attraverso chiamate diverse alla stessa subroutine / funzione. Altrimenti normalmente quando si ritorna da una subroutine / funzione, le variabili “locali” perdono i loro valori poiché la memoria in cui sono stati memorizzati quei vars è stata rilasciata. È come static in C, se conosci questa lingua.