Как простой необязательный аргумент может привести к повреждению данных?

У меня есть код FORTRAN с подпрограммой:

SUBROUTINE READ_NC_VALS(NCID, RECID, VARNAME, VARDATA)
integer ncid, recid
character*(*) varname
real*8 vardata
dimension vardata(15,45,75)

etc.

Я хочу добавить немного гибкости к этому коду, и я подумал, что сделаю это сначала добавив необязательный аргумент flag:

SUBROUTINE READ_NC_VALS(NCID, RECID, VARNAME, VARDATA, how_to_calculate)

! everything the same and then ...
logical, optional :: how_to_calculate

Теперь, на этом этапе, я даже не использую "how_to_calculate". Я просто помещаю это в код для тестирования. Поэтому я скомпилировал код без заминки. Затем я запускаю его и получаю ошибку в подпрограмме. В частности, некоторые значения в коде позже "волшебным образом" изменяются по сравнению с тем, что было без этого необязательного аргумента. Новые значения не имеют смысла для логики кода, поэтому он вежливо завершает работу с сообщением об ошибке. Позвольте мне еще раз подчеркнуть, что на данный момент я даже не использую этот необязательный аргумент. Итак, в порядке вещей, я возвращаюсь ко всем местам в источнике, которые вызывают эту подпрограмму, и, хотя мой новый аргумент необязательный, я вставляю значения для него во всех вызовах. Когда я это делаю, код работает нормально. Ну что, как поживаешь? Как простое присутствие неиспользуемого необязательного аргумента в подпрограмме может привести к повреждению других данных? И как добавление входных параметров для этого необязательного аргумента может исправить ситуацию? Кстати, это компилируется с PGI.

Есть идеи? Благодарю.

Кстати, извините, что не предоставил больше кода. Мой босс может быть не очень доволен мной, если я это сделаю. Я не делаю правила; Я просто работаю здесь.

2 ответа

Решение

Необязательные аргументы в Фортране реализуются путем передачи 0 (нулевой указатель) для каждого необязательного аргумента, который не имеет значения, предоставляемого вызывающей подпрограммой. Из-за этого подпрограммы, которые принимают необязательные аргументы, должны:

  • либо есть явный INTERFACE определение внутри вызывающей подпрограммы
  • или быть подпрограммой уровня модуля (для них интерфейсы генерируются автоматически)

Если вы добавите необязательный аргумент в подпрограмму, но он не имеет интерфейса в вызывающей программе или не является подпрограммой уровня модуля, то компилятор не будет генерировать правильную вызывающую последовательность - он будет передавать меньше аргументов, чем ожидалось. Это может создать проблему в системах Unix, где PGI передает длину всех CHARACTER*(*) аргументы в конце списка аргументов (в Windows длина передается как следующий аргумент после адреса строки). Один отсутствующий аргумент сместит размещение аргументов длины в стеке (или поместит их в неправильные регистры на x64), что приведет к неправильной длине VARNAME полученная строка READ_NC_VALS, Это может привести ко всем видам плохого поведения, включая перезапись памяти и "магическое" изменение значений, которые не должны изменяться в соответствии с логикой программы.

Очевидно, что просто добавление фиктивного аргумента в подпрограмму, с или без optional атрибут, не должно вызывать никаких проблем. Однако может случиться так, что изменение выявит еще одну проблему с кодом, который уже был там, но не вызвал каких-либо видимых негативных последствий.

Без дополнительного кода все, что мы можем сделать, - это догадаться, что обычно не очень полезно. Одна вещь, которая приходит на ум, это необходимость явного интерфейса при использовании необязательных аргументов. Код организован в модули?

Другие вопросы по тегам