Принудительно применять объявленные переменные 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
мои вопросы:
- это нормальное поведение для всех компиляторы?
- есть ли способ заставить компиляторы гарантировать, что переменная действительно постоянна и что изменения будут представлены как ошибки компилятора? Я имею в виду что-то вроде ключевого слова const в C/C++, которое также проверяется на вызываемые функции, которые также должны гарантировать, что константа обрабатывается соответственно и что никакая ссылка не ускользает.
- Я нашел возможность передать переменную подпрограмме по "значению", передав ее через выражение типа
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) будут вести себя так же.