Принудительно применять объявленные переменные intent(in) в Fortran как константы также в вызываемых подпрограммах / функциях

в подпрограмме или функции входная переменная может быть определена с намерением (in), и компилятор гарантирует, что в подпрограмме переменная не может быть изменена. Как только переменная передается (по ссылке) другой подпрограмме, эта подпрограмма может изменять переменную без предупреждения компилятора.

Это было протестировано с gfortran с кодом:

program Test
    integer i
    i = 21 ! half the truth
    call test(i)
    write (*,*) "21 expected, but is 42: ", i
end program

subroutine test(i)
    integer, intent(in) :: i
    call doSomethingNasty(i)
end subroutine

subroutine doSomethingNasty(i)
    integer :: i
    i = 42 ! set the full truth ;-)
end subroutine

мои вопросы:

  1. это нормальное поведение для всех компиляторы?
  2. есть ли способ заставить компиляторы гарантировать, что переменная действительно постоянна и что изменения будут представлены как ошибки компилятора? Я имею в виду что-то вроде ключевого слова const в C/C++, которое также проверяется на вызываемые функции, которые также должны гарантировать, что константа обрабатывается соответственно и что никакая ссылка не ускользает.
  3. Я нашел возможность передать переменную подпрограмме по "значению", передав ее через выражение типа test((i)). Для числовых переменных это понятно и нормально, но это, похоже, работает с gfortran для массивов, производных типов и указателей. Работает ли это с другими компиляторами? Это безопасный способ защитить мои локальные переменные?

2 ответов


С достаточными параметрами компилятора gfortran генерирует предупреждение для вашего примера, что используется неявный интерфейс.

Если вы сделаете интерфейс явным, поместив подпрограммы в модуль, и используете intents для всех аргументов, gfortran поймает проблему:

module mysubs

contains

subroutine test(i)
    integer, intent(in) :: i
    call doSomethingNasty(i)
end subroutine

subroutine doSomethingNasty(i)
    integer, intent (inout) :: i
    i = 42 ! set the full truth ;-)
end subroutine


end module mysubs


program Test_intent_in

use mysubs

    integer i
    i = 21 ! half the truth
    call test(i)
    write (*,*) "21 expected, but is 42: ", i

end program Test_intent_in

gfortran выдает сообщение об ошибке:

call doSomethingNasty(i)
                          1
Error: Procedure argument at (1) is INTENT(IN) while interface specifies INTENT(INOUT)

при передаче аргумента "(i) " Вы передаете выражение, а не переменную. Выражение не поддается определению и, таким образом, не следует использовать в качестве фактического аргумента для фиктивного аргумента" out "или" inout".

другой подход к аргументу "безопасность": вы также можете использовать атрибут" value " в объявлении фиктивного аргумента, чтобы по существу сделать локальную копию аргумента и гарантировать, что фактический аргумент не будет изменен.

изменить: Как отметил kemiisto, "содержит" также делает интерфейс известным. Мне не нравится "содержит", потому что переменная область видимости ... все переменные родительская программа видна. Попробуйте этот тестовый код:

PROGRAM contains_tst

  INTEGER :: i, m

  i = 21
  m = 22
  CALL test(m)

  CONTAINS

    SUBROUTINE test(j)
      INTEGER, INTENT(IN) :: j
      write (*, *) i, j
    END SUBROUTINE test

END PROGRAM contains_tst

Как только переменная будет передана (по ссылке)

предупреждение: стандарт Fortran не указывает, как передаются переменные (по ссылке, по значению или любым другим способом). Это зависит от реализации. Fortran сильно отличается от C/C++. Лучше перестать думать в сторону. Это будет заблуждением.

1) да и нет. Это зависит от реализации. Прежде всего атрибут INTENT определяет ваши намерения. Как вы можете видеть в Fortran стандарт, раздел 5.3.10, Примечание 5.17 (вы можете получить окончательный проект так называемого Fortran 2008 по ссылке в начале этой страницы http://fortranwiki.org/fortran/show/Fortran+2008):

спецификации намерения аргумента служат нескольким целям в дополнение к документирование предполагаемого использования фиктивных аргументов. Процессор может проверить используется ли фиктивный аргумент INTENT (IN) таким образом, чтобы переопределите его. [...]

компилятор ("процессор") can (не должны) проверять такие вещи.

во-вторых(как я уже упоминал), вы не можете быть уверены, что для аргумента с INTENT (IN) компилятор решит передать его по значению, а не по ссылке. В данном случае выбор был сделан по ссылке. По крайней мере, кажется, что я в тестовой подпрограмме прошел по ссылке. Следующая подпрограмма. Намерение по умолчанию-INOUT. Вот почему можно изменить значение аргумента i (с неопределенным, поэтому default Умысел) в досометингнасти. И снова меня пропустили мимо ушей. Или, может быть, это даже было "копирование/копирование". Такая свобода существует, чтобы позволить компилятору выполнять оптимизацию.

2) нет. Если я правильно вас понял, вам нужно что-то похожее на постоянные ссылки (для достижения правильности "const"). Но у нас даже нет ссылок в Fortran, поэтому, очевидно, нет постоянных ссылок.

3)Существует способ защиты локальных переменных. С. Б. М. указал в своем ответе поместите свои подпрограммы в модули (или в раздел содержит основную программу) и всегда указывайте атрибуты намерения для переменных. Я попытался скомпилировать код ниже с различными компиляторами Fortran, доступными мне

PROGRAM main

  INTEGER :: i

  i = 21
  CALL test(i)
  WRITE (*,*) "21 expected, but is 42: ", i

  CONTAINS

    SUBROUTINE test(i)
      INTEGER, INTENT(IN) :: i
      CALL do_something_nasty(i)
    END SUBROUTINE test

    SUBROUTINE do_something_nasty(i)
      INTEGER, INTENT(INOUT) :: i
      i = 42
    END SUBROUTINE do_something_nasty

END PROGRAM main

и все компиляторы (GNU, Intel, Open64, Portland и g95) выдали сообщение об ошибке. Я думаю, что другие компиляторы (Pathscale, IBM) будут вести себя так же.