Функция Fortran, возвращающая размещаемый массив - PullRequest
3 голосов
/ 07 ноября 2019

Позвольте мне рассмотреть функцию, возвращающую массив allocatable. Должна ли переменная массива, содержащая результат (вне функции) быть выделена перед присваиванием?

Рассмотрим, например, следующую программу

program mem
  implicit none

  interface
     function get_matrix() result(res)
       real(kind=kind(0.d0)), allocatable :: res(:,:)
     end function get_matrix
  end interface

  real(kind=kind(0.d0)), allocatable :: w(:,:)

  allocate(w(2,2)) ! Is this necessary?
  w=get_matrix()
  deallocate(w)

end program mem

function get_matrix() result(res)
  real(kind=kind(0.d0)), allocatable :: res(:,:)

  allocate(res(2,2))
  res = 0.d0
  res(1, 1) = 1.d0
  res(2, 2) = 1.d0
end function get_matrix

Согласно this this , массив res, выделенный для результата get_matrix, освобождается, как только выходит из области видимости.

Будет ли присвоение нераспределенной переменной w в основной программе продлить область действия результата get_matrix? Другими словами, если я опущу allocate(w(2,2)) в основной программе, получу ли я неопределенное поведение?

Пропуск allocate(w(2,2)) и компиляция с gfortran 9.2.0 и параметрами -Wall -std=f2008 выдает следующие предупреждения

mem.f90:13:0:

   13 |   w=get_matrix()
      | 
Warning: ‘w.offset’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].lbound’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].ubound’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].lbound’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].ubound’ is used uninitialized in this function [-Wuninitialized]
mem.f90:13:0:

   13 |   w=get_matrix()
      | 
Warning: ‘w.dim[0].lbound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].ubound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].lbound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].ubound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].ubound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[0].lbound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].ubound’ may be used uninitialized in this function [-Wmaybe-uninitialized]
mem.f90:13:0: Warning: ‘w.dim[1].lbound’ may be used uninitialized in this function [-Wmaybe-uninitialized]

Тем не менее, запуск программы с valgrind, а также компиляция с -fbounds-check, -fsanitize=address или -fsanitize=leak не дает никаких ошибок. Кроме того, инструкция deallocate(w) в конце не приводит к сбою программы, предполагая, что w содержит память, выделенную на get_matrix, и, следовательно, нет необходимости выделять w в основной программе.

В то же время включение в код allocate(w(2,2)) подавляет предупреждение компилятора. Несмотря на то, что создается впечатление, что одна и та же память выделяется дважды, valgrind не сообщает об утечке памяти и фактически сообщает об одинаковом использовании памяти.

Как правильно хранить allocatableмассив как результат функции? Нужно ли выделять w перед сохранением в нем результата get_matrix?

Ответы [ 3 ]

2 голосов
/ 07 ноября 2019

Нет необходимости предварительно выделять или освобождать. Об этом позаботится компилятор. Следующий код работает должным образом с Intel fortran.

program Console1
implicit none
! Variables
real(8), allocatable :: A(:,:)
integer :: i, j

! Body of Console1

A = get_matrix(6,4)

do i=1, size(A,1)
    print '(*(g9.4),1x)', A(i,:)
end do    

contains

function get_matrix(n,m) result(res)
integer, intent(in) :: n,m
real(8), allocatable :: res(:,:)
integer :: i

    allocate(res(n,m))
    res = 0d0
    forall(i=1:min(n,m)) res(i,i)=1d0

end function

end program Console1

Вывод:

1.000    0.000    0.000    0.000
0.000    1.000    0.000    0.000
0.000    0.000    1.000    0.000
0.000    0.000    0.000    1.000
0.000    0.000    0.000    0.000
0.000    0.000    0.000    0.000

PS. Поместите функции в объявление программы, используя ключевое слово contains. Таким образом, они не являются внешними функциями, и объявление интерфейса не требуется.

1 голос
/ 07 ноября 2019

Ответы от ja72 и Владимира Ф. верны. Однако для полноты излагаю еще один момент. В операторе

var = function_ref()

, где правая часть является ссылкой на функцию с выделяемым результатом, на самом деле в этом нет ничего особенного в отношении выделяемой природы. Это выражение , а не выделяемая сущность.

Итак, наше назначение очень похоже на любое другое

var = expr

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

В случае вопроса применяются обычные внутренние правила присваивания. В частности, нам не нужно выделять w перед назначением.


On

Кроме того, инструкция deallocate(w) в конце не приводит к сбоюпрограмма, предполагающая, что w содержит память, выделенную get_matrix, и, следовательно, нет необходимости выделять w в основной программе.

Есть и другие вещи, которые нужно сказать. Результат функции get_matrix сам по себе освобождается после использования в назначении. w является отдельной сущностью от результата функции, и внутреннее присвоение вызывает выделение w.

Итак, нет, вы не «продлеваете область действия результата»: у вас есть скопировал это, во вновь выделенную переменную, а затем освободил, как только он закончилВместо этого рассмотрим выражение типа

var = function_ref() + 1

Опять же, у нас есть выражение для присваивания, но ожидаем ли мы, что результат функции "сохранится"?

Также рассмотрим этот связанный вопрос .

1 голос
/ 07 ноября 2019

Есть много похожих ошибок в bugzilla GCC, и они считаются проблемой с вашим компилятором, а не с вашим кодом. Некоторые уже исправлены, некоторые не 67679 66459 88455 и более (много дубликатов).

Попробуйте самую последнюю версию компилятора, но это все еще может сохраняться.

В качестве обходного пути я использую -Wno-maybe-uninitialized.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...