2010-08-18 19 views
5

Después de mucha depuración dolorosa, creo que he encontrado una propiedad única de Fortran que me gustaría verificar aquí en stackoverflow.¿Fortran conserva el valor de las variables internas a través de llamadas de función y subrutina?

Lo que he notado es que, al menos, el valor de las variables lógicas internas se conserva a través de llamadas a funciones o subrutinas.

Aquí es un código de ejemplo para ilustrar mi punto:

PROGRAM function_variable_preserve 
IMPLICIT NONE 

CHARACTER(len=8) :: func_negative_or_not ! Declares function name 
INTEGER :: input 
CHARACTER(len=8) :: output 

input = -9 

output = func_negative_or_not(input) 
WRITE(*,10) input, " is ", output 
10 FORMAT("FUNCTION: ", I2, 2A) 

CALL sub_negative_or_not(input, output) 
WRITE(*,20) input, " is ", output 
20 FORMAT("SUBROUTINE: ", I2, 2A) 

WRITE(*,*) 'Expected negative.' 


input = 7 
output = func_negative_or_not(output) 
WRITE(*,10) input, " is ", output 

CALL sub_negative_or_not(input, output) 
WRITE(*,20) input, " is ", output 

WRITE(*,*) 'Expected positive.' 

END PROGRAM function_variable_preserve 

CHARACTER(len=*) FUNCTION func_negative_or_not(input) 
IMPLICIT NONE 

INTEGER, INTENT(IN) :: input 
LOGICAL :: negative = .FALSE. 

IF (input < 0) THEN 
    negative = .TRUE. 
END IF 

IF (negative) THEN 
    func_negative_or_not = 'negative' 
ELSE 
    func_negative_or_not = 'positive' 
END IF 

END FUNCTION func_negative_or_not 

SUBROUTINE sub_negative_or_not(input, output) 
IMPLICIT NONE 

INTEGER, INTENT(IN) :: input 
CHARACTER(len=*), INTENT(OUT) :: output 
LOGICAL :: negative = .FALSE. 

IF (input < 0) THEN 
    negative = .TRUE. 
END IF 

IF (negative) THEN 
    output = 'negative' 
ELSE 
    output = 'positive' 
END IF 

END SUBROUTINE sub_negative_or_not 

Ésta es la salida:

FUNCTION: -9 is negative 
SUBROUTINE: -9 is negative 
Expected negative. 
FUNCTION: 7 is negative 
SUBROUTINE: 7 is negative 
Expected positive. 

Como se puede ver, parece que una vez que la función o subrutina se llama una vez, la variable lógica negative, si se cambia a .TRUE., permanece como tal a pesar de la inicialización de negative a .FALSE. en la declaración de declaración de tipo.

Podría, por supuesto, corregir este problema simplemente agregando una línea negativa = .FALSE. después de declarar la variable en mi función y subrutina.

Sin embargo, me parece muy extraño que sea necesario.

Por razones de portabilidad y reutilización del código, ¿no debería el lenguaje (o el compilador quizás) requerir la reinicialización de todas las variables internas cada vez que se llama a la subrutina o función?

Respuesta

9

Respondiendo a tu pregunta: Sí Fortran conserva el valor de las variables internas a través de llamadas de función y subrutina.

Bajo ciertas condiciones ...

Si declara una variable interna con el atributo SAVE, su valor se guarda de una llamada a la siguiente. Esto es, por supuesto, útil en algunos casos.

Sin embargo, su pregunta es una reacción común al conocer por primera vez uno de los errores de Fortran: si inicializa una variable interna en su declaración, automáticamente adquiere el atributo SAVE. Has hecho exactamente eso en tus subrutinas. Esto es estándar-conforme. Si no desea que esto suceda, no inicialice en la declaración.

Esta es la causa de muchas sorpresas y quejas de (algunos) recién llegados al idioma. Pero no importa cuánto se quejen, no va a cambiar, así que solo tienes que (a) saberlo y (b) programar teniendo conciencia de ello.

+0

Creo (para asegurarme de que tendría que verificar el Estándar, que no tengo ahora mismo) que a partir de F2003, todas las variables tienen el atributo SAVE. – Rook

+0

¡Sorprendente! – EMiller

3

Esto no es muy diferente de static variables de ámbito funcional en C o C++.

El diseño del lenguaje de programación estaba en su infancia, cuando FORTRAN era desarrollado. Si se diseñó desde cero hoy, sin duda muchas de las decisiones de diseño habrían sido diferentes.

Originalmente, FORTRAN ni siquiera apoyar la recursividad, no hubo asignación dinámica de memoria , programas desempeñado todo tipo de juegos de tipo juego de palabras con COMMON bloques y EQUIVALENCE declaraciones, procedimientos podrían tener múltiples puntos de entrada .... por lo el modelo de memoria era básicamente para que el compilador/enlazador distribuyera todo, incluso las variables locales y las constantes literales numéricas, en ubicaciones de almacenamiento fijas, en lugar de en la pila . Si quisiera, incluso podría escribir un código que cambiara el valor de "2" a "42"!

Por ahora, existe una gran cantidad de código FORTRAN heredado, y los escritores de compiladores hacen todo lo posible para preservar la semántica compatible con versiones anteriores. No puedo citar el capítulo y el versículo de la norma que ordena el comportamiento que ha observado, ni su fundamento, pero parece razonable que la compatibilidad con versiones anteriores superó las sensibilidades del diseño del lenguaje moderno, en este caso.

+0

Sin duda, hay algunos problemas heredados en fortran que, si se escribieran hoy desde strach, se manejarían de forma diferente, pero por favor, no lo haga parecer que es un lenguaje heredado. Todo lo contrario: en muchos términos, tiene características que otros lenguajes "modernos" no han adquirido (por ejemplo, en c.l.f. las discusiones a menudo están relacionadas con f03/el más nuevo estándar C, ¿cuál es C99?) – Rook

+0

@Idigas: ¡buenos puntos! No quise dar a entender que FORTRAN es un fósil ... es solo que el aspecto del legado parecía estar más relacionado con la pregunta de CmdrGuard, así que eso es en lo que me concentré. –

+0

Obviamente, lo malentendí; Lo siento por eso. Mi error. El resto de tu respuesta está bastante bien puesta. – Rook

3

Esto ha sido discutido varias veces aquí, y más recientemente en Fortran assignment on declaration and SAVE attribute gotcha

Usted no tiene que descubrir este comportamiento mediante la experimentación, se afirma claramente en los mejores libros de texto.

Los diferentes idiomas son diferentes y tienen comportamientos diferentes.

Existe un motivo histórico para este comportamiento. Muchos compiladores para Fortran 77 y anteriores conservaron los valores de TODAS las variables locales a través de llamadas de procedimientos. Se suponía que los programadores no debían confiar en este comportamiento, pero muchos lo hicieron. Según el estándar, si quería una variable local (no COMÚN) para conservar su valor, necesitaba usar "GUARDAR". Pero muchos programadores ignoraron esto. En esa época, los programas se transferían con menos frecuencia a diferentes plataformas y compiladores, por lo que las suposiciones incorrectas podrían nunca notarse. Es común encontrar este problema en los programas heredados: los compiladores actuales de Fortran suelen proporcionar un interruptor de compilación para hacer que se guarden todas las variables. Sería tonto que el estándar de lenguaje requiera que todas las variables locales conserven sus valores. Pero un requisito intermedio que rescataría a muchos programas que fueron descuidados con "SAVE" sería requerir que todas las variables inicializadas en sus declaraciones tengan automáticamente el atributo SAVE. Por lo tanto, lo que descubrió ...

Cuestiones relacionadas