Pointers 指向第0个索引的指针有问题

Pointers 指向第0个索引的指针有问题,pointers,fortran,Pointers,Fortran,对于上面的代码,我得到以下输出: module shared !derived type target here integer, parameter :: nblock = 2 integer, parameter :: xdim = 2 TYPE block_info INTEGER :: il=10,jl=20,kl=30 REAL, ALLOCATABLE :: x(

对于上面的代码,我得到以下输出:

module shared
!derived type target here
integer, parameter :: nblock = 2
integer, parameter :: xdim   = 2

TYPE block_info
    INTEGER                               :: il=10,jl=20,kl=30
    REAL, ALLOCATABLE                     :: x(:)
END TYPE block_info
TYPE(block_info), TARGET :: block(nblock)

end module shared


module point_to
!point to subroutine here
use shared

REAL, POINTER :: x(:)
integer       :: il,jl,kl

contains

subroutine set_current(n)
    nullify(x)
    il = block(n)%il
    jl = block(n)%jl
    kl = block(n)%kl
    x => block(n)%x(0:xdim)
end subroutine set_current

end module point_to


program main

use shared
use point_to

!Iam allocating derived type target and initialize
do i = 1, nblock
    allocate(block(i)%x(0:xdim))
    do j = 0, xdim
        block(i)%x(j) = dble(i)*dble(j)
    enddo
enddo

!Iam pointing using set_current subroutine and print
do i = 1, nblock
    call set_current(i)
    do j = 0, xdim
        write(*,*) "i= ",i, "j= ",j, block(i)%x(j), x(j)
    enddo
enddo


end program main

我已经应用了从1开始的x向量到xdim,没有得到任何错误。当第一个索引被选择为0时,问题就开始了。在上述输出中,最后两个值必须相等。那么问题出在哪里呢?

首先,当您遇到问题时,使用编译器的所有调试功能,仅仅查看代码是不够的

例如:

 i=            1 j=            0   0.00000000       0.00000000    
 i=            1 j=            1   1.00000000       0.00000000    
 i=            1 j=            2   2.00000000       1.00000000    
 i=            2 j=            0   0.00000000       0.00000000    
 i=            2 j=            1   2.00000000       0.00000000    
 i=            2 j=            2   4.00000000       2.00000000
编译器直接告诉您错误在哪里。数组x从元素1开始,您试图访问元素0,因此超出了边界。它发生在第52行,这是

> gfortran -g -fbacktrace -fcheck=all -Wall zeroptr.f90 
zeroptr.f90:44.24:

        block(i)%x(j) = dble(i)*dble(j)
                        1
Warning: Possible change of value in conversion from REAL(8) to REAL(4) at (1)
> ./a.out 
At line 52 of file zeroptr.f90
Fortran runtime error: Index '0' of dimension 1 of array 'x' below lower bound of 1

Error termination. Backtrace:
#0  0x7fa2d4af3607 in ???
#1  0x7fa2d4af4115 in ???
#2  0x7fa2d4af44ba in ???
#3  0x4014dc in MAIN__
        at /home/lada/f/testy/stackoverflow/zeroptr.f90:52
#4  0x401640 in main
        at /home/lada/f/testy/stackoverflow/zeroptr.f90:37
为什么会这样?因为

   write(*,*) "i= ",i, "j= ",j, block(i)%x(j), x(j)
使x指向块n%x0:xdim,但x将从1开始

在Fortran 2003中,可以进行以下指针重新映射:

  x => block(n)%x(0:xdim)
x => block(n)%x  ! x has lower bound 0
x => block(n)%x(0:xdim)  ! x has lower bound 1
它的工作原理与预期一样,但最好使用blockn%x。

解释了问题的根源。然而,这个细节在这里的问题中经常出现,所以值得强调几点

数组部分,即使它匹配数组的所有元素,也与整个数组不同。blockn%x是一个完整的数组;blockn%x0:xdim是数组部分,即使该数组组件的边界为0和xdim

为什么这种差异很重要?让我们看看指针赋值的规则。指针赋值语句

 x(0:xdim) => block(n)%x(0:xdim)
是数据指针赋值的一种。正如另一个答案所提到的,有一件事叫做边界重新映射。也可以给出边界规范

这两种情况在这里都没有发生,稍后我将进一步讨论,因此我们将看到Fortran 2008,7.2.2.3

如果出现边界规范列表,则指定下限;否则,每个维度的下限是应用于指针目标对应维度的内在函数LBOUND 13.7.90的结果

LBOUND的结果是,整个数组/数组部分的区别非常重要,Fortran 2008,13.7.90:

如果数组是整个数组。。。LBOUND ARRAY,DIM的值等于数组的下标DIM的下限。否则,结果值为1

这意味着在这种情况下,LBOUNDblockn%x的结果为0,但LBOUNDblockn%x0:xdim的结果始终为1

这意味着

x => block(n)%x(0:xdim)
现在,Vladimir F提到边界重新映射:

  x => block(n)%x(0:xdim)
x => block(n)%x  ! x has lower bound 0
x => block(n)%x(0:xdim)  ! x has lower bound 1
这表示x具有所请求的上限和下限。这是很合理的,但也有几个警告:

一个人必须小心地重复界限;左侧大小的元素数必须最多与右侧大小的元素数相同,如果较少,则为较小的数组。 右侧必须是秩1或简单相邻。 所有这些条件都成立,但这使得推广需要谨慎

最后,还有一个边界规范:

x(0:xdim) => block(n)%x(0:xdim)
这仍然指定下限,但指针和目标始终具有相同的大小,并且对秩/邻接性没有限制


总结:对于这个问题的简单情况,使用x=>blockn%x

你用了标签。Fortran问题始终使用tag。您可以添加另一个标记来区分版本。但是,您的代码不是Fortran 90。原始Fortran 95甚至不允许将可分配组件分配给派生类型,这是Fortran 2003的一项功能。所以您的代码是Fortran 2003。设置x0:xdim=>blockn%x0:xdim解决了我的问题。非常感谢。顺便说一下,我正在使用GNU Fortran Ubuntu 4.8.4-2ubuntu1~14.04.3 4.8.4版本,但是调试选项没有如您所说的那样工作,没有给出错误。这是如何发生的?再次感谢你。我的版本是4.8.5,所以它应该像我所展示的那样工作。运行为调试而编译的代码时会显示错误!这么好的解释谢谢你,先生,现在一切都清楚了