为什么这个Fortran模块接口会根据使用的函数数量给出不同的结果?

时间:2014-03-10 22:03:07

标签: fortran gfortran generic-programming allocatable-array

我编写了一个模块,其中包含一个名为'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无法进入模块函数。

如果我只是评论我被推到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     

再次如预期那样。

当我将第三个循环注释到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. 在我注释掉第三个和第四个循环的情况下,为什么(100)和b(100)的结果都等于0?

  3. 任何反馈都将不胜感激......谢谢!

    编辑:

    需要更改的两个功能如下所示

      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
    

2 个答案:

答案 0 :(得分:1)

您在某些函数体中分配语句引用array参数的大小。如果未分配array参数,则该引用无效。

在程序的早期,您测试分配状态并设置名为length的变量 - 也许您打算使用它。

(为清楚起见 - 或许可以查看push_scalar_int_onto_rank1_int函数中的allocate语句。)

答案 1 :(得分:1)

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行是returnpush_scalar_int_onto_rank1_int之前的行。