Почему интерфейс этого модуля Fortran дает разные результаты в зависимости от того, сколько его функций используется?

Я написал модуль, который содержит интерфейс под названием «push», который помещает значения в распределяемые массивы. Я хочу, чтобы у него было общее поведение, чтобы я мог добавлять новую функцию для данного типа в интерфейс push по мере необходимости. Проблема в том, что по мере роста количества функций для данного интерфейса растет и странное поведение push-интерфейса.

Код для модуля (push_array.f90):

module push_array
  implicit none
  ! usage:
  ! array = push(array,val)
  interface push
     module procedure push_scalar_int_onto_rank1_int
     module procedure push_scalar_int2_onto_rank1_int2
     module procedure push_rank1_int_onto_rank2_int
     module procedure push_rank1_real8_onto_rank2_real8
  end interface push

contains
  function push_scalar_int_onto_rank1_int (array,val) result (new_array)
    integer,intent(in),allocatable :: array(:)
    integer,intent(in) :: val
    integer,allocatable :: new_array(:)
    integer :: length
    if (allocated(array)) then
       length = size(array) + 1
    else
       length = 1
    end if
    allocate(new_array(size(array) + 1))
    if (allocated(array)) new_array(:) = array(:)
    new_array(length) = val
    return
  end function push_scalar_int_onto_rank1_int

  function push_scalar_int2_onto_rank1_int2 (array,val) result (new_array)
    integer(2),intent(in),allocatable :: array(:)
    integer(2),intent(in) :: val
    integer(2),allocatable :: new_array(:)
    integer :: length
    if (allocated(array)) then
       length = size(array) + 1
    else
       length = 1
    end if
    allocate(new_array(size(array) + 1))
    if (allocated(array)) new_array(:) = array(:)
    new_array(length) = val
    return
  end function push_scalar_int2_onto_rank1_int2

  function push_rank1_int_onto_rank2_int (array,val) result (new_array)
    integer,intent(in),allocatable :: array(:,:)
    integer,intent(in) :: val(:)
    integer,allocatable :: new_array(:,:)
    integer :: length
    if (allocated(array)) then
       length = size(array,2) + 1
    else
       length = 1
    end if
    allocate(new_array(1:size(val),length))
    if (allocated(array)) new_array(1:size(val),:) = array(1:size(val),:)
    new_array(1:size(val),length) = val
    return
  end function push_rank1_int_onto_rank2_int

    function push_rank1_real8_onto_rank2_real8 (array,val) result (new_array)
    real(8),intent(in),allocatable :: array(:,:)
    real(8),intent(in) :: val(:)
    real(8),allocatable :: new_array(:,:)
    integer :: length
    if (allocated(array)) then
       length = size(array,2) + 1
    else
       length = 1
    end if
    allocate(new_array(1:size(val),length))
    if (allocated(array)) new_array(1:size(val),:) = array(1:size(val),:)
    new_array(1:size(val),length) = val
    return
  end function push_rank1_real8_onto_rank2_real8

end module push_array

Тестовый код (test_push_array.f90):

program main
  use push_array, only: push
  implicit none
  integer,allocatable :: a(:)
  integer(2),allocatable :: b(:)
  integer,allocatable :: c(:,:)
  real(8),allocatable :: d(:,:)
  integer :: xp(3)
  real(8) :: xp8(3)
  integer :: i
  integer(2) :: j
  ! test that a scalar integer can be pushed onto a rank1 integer array
  do i=1,100
     a = push(a,i)
  end do
  print *, a(1),a(100)

  ! test that a scalar integer(2) can be pushed onto a rank1 integer(2) array
  do j=1,100
     b = push(b,j)
  end do
  print *, b(1),b(100)

  ! test that a rank1 integer can be pushed onto a rank2 integer
  do i=1,100
     xp = [i,i+1,i+2]
     c = push(c,xp)
  end do
  print *, c(1:3,1),c(1:3,100)

  ! test that a rank1 real(8) can be pushed onto a rank2 real(8)
  do i=1,100
     xp8 = [i + 0.001,i + 0.002, i + 0.003]
     d = push(d,xp8)
  end do
  print *, d(:,1),d(:,100)

end program main

сделать вывод, чтобы показать флаги компилятора:

$ make
gfortran -g -O2 -c push_array.f90
gfortran -g -O2 -o main test_push_array.f90 push_array.o

Моя версия компилятора:

$ gfortran --version
GNU Fortran (GCC) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.

Моя система:

$ uname -a
Darwin darthan 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64

Если я запускаю тестовый код с заданным значением, он переходит в бесконечный цикл, и моя системная память полностью исчерпывается. Я попытался отследить тестовый пример в gdb, установив точку останова, в которой я нажимаю i на a в первом цикле, но gdb не может войти в функцию модуля.

Если я прокомментирую только первый тестовый цикл, в котором i помещается в a, вот результаты:

$ ./main
      1    100
           1           2           3         100         101         102
   1.0010000467300415        1.0019999742507935        1.0030000209808350        100.00099945068359        100.00199890136719        100.00299835205078     

Этого следовало ожидать.

Если я закомментирую только второй цикл, в котором я нажимаю j на b, вот результаты:

$ ./main
           1         100
           1           2           3         100         101         102
   1.0010000467300415        1.0019999742507935        1.0030000209808350        100.00099945068359        100.00199890136719        100.00299835205078     

Еще раз, как и ожидалось.

Все начинает становиться странным, когда я комментирую только третий цикл, в котором я нажимаю xp на c:

$ ./main
           1           0
      1      0
   1.0010000467300415        1.0019999742507935        1.0030000209808350        100.00099945068359        100.00199890136719        100.00299835205078     

Шаблон продолжается, когда я закомментирую только четвертый цикл, в котором я нажимаю xp8 на d:

$ ./main
           1           0
      1      0
           1           2           3         100         101         102

Мои вопросы:

  1. Почему основная тестовая программа заходит в бесконечный цикл, когда я пытаюсь использовать все четыре функции, определенные в интерфейсе push, в одной и той же программе?

  2. В случае, когда я закомментировал третий и четвертый циклы, почему результаты для a (100) и b (100) оба равны 0?

Любая обратная связь будет оценена ... спасибо!

Редактировать:

Две функции, которые необходимо изменить, приведены ниже.

  function push_scalar_int_onto_rank1_int (array,val) result (new_array)
    integer,intent(in),allocatable :: array(:)
    integer,intent(in) :: val
    integer,allocatable :: new_array(:)
    integer :: length
    if (allocated(array)) then
       length = size(array) + 1
    else
       length = 1
    end if
    allocate(new_array(length)) ! changed
    if (allocated(array)) new_array(:) = array(:)
    new_array(length) = val
    return
  end function push_scalar_int_onto_rank1_int

  function push_scalar_int2_onto_rank1_int2 (array,val) result (new_array)
    integer(2),intent(in),allocatable :: array(:)
    integer(2),intent(in) :: val
    integer(2),allocatable :: new_array(:)
    integer :: length
    if (allocated(array)) then
       length = size(array) + 1
    else
       length = 1
    end if
    allocate(new_array(length)) ! changed
    if (allocated(array)) new_array(:) = array(:)
    new_array(length) = val
    return
  end function push_scalar_int2_onto_rank1_int2

person user600830    schedule 10.03.2014    source источник


Ответы (2)


Оператор allocate в некоторых телах функций ссылается на размер аргумента array. Если аргумент array не выделен, эта ссылка недействительна.

Ранее в процедуре вы проверяете статус распределения и устанавливаете переменную с именем length - возможно, вы хотели это использовать.

(Для ясности - возможно, посмотрите на оператор allocate в функции push_scalar_int_onto_rank1_int.)

person IanH    schedule 10.03.2014
comment
Если я печатаю, выделен ли возвращаемый массив a в цикле основной программы для gfortran, он возвращает true, а ifort возвращает false. - person Exascale; 11.03.2014
comment
Проблема заключается в начальном статусе распределения a - к тому времени, когда функция вернется на первой итерации цикла, все уже испортится. - person IanH; 11.03.2014

IanH объясняет проблему. Компилятор может помочь вам найти это. Я получаю разные ответы от gfortran 4.8 в зависимости от параметров компиляции. С -O2 -fimplicit-none -Wall -Wline-truncation -Wcharacter-truncation -Wsurprising -Waliasing -Wimplicit-interface -Wunused-parameter -fcheck=all -std=f2008 -pedantic -fbacktrace он компилируется, а затем выдает ошибку времени выполнения:

At line 25 of file push.f90
Fortran runtime error: Index '1' of dimension 1 of array 'new_array' above upper bound of -265221874

строка 25 - это строка перед return в push_scalar_int_onto_rank1_int.

person M. S. B.    schedule 10.03.2014
comment
Как ни странно, я не получал никаких сообщений об ошибках во время выполнения, используя эти флаги, все равно просто зависает. Было бы неплохо, если бы его бросили! - person user600830; 11.03.2014