第0个索引的指针有问题

时间:2017-02-21 14:37:08

标签: 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(:)
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

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

 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开始应用到xdim,但没有出现错误。当第一个索引被选为0时,问题就开始了。在上面的输出中,最后两个值必须相等。那么问题出在哪里?

2 个答案:

答案 0 :(得分:3)

Vladimir F's answer解释了问题的根源。但是,这个细节在这里的问题中经常出现,因此值得强调一些事情。

数组部分,即使它匹配数组的所有元素,也与整个数组不同。 block(n)%x是一个完整的数组; block(n)%x(0:xdim)是一个数组部分(即使该数组组件已绑定0xdim)。

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

x => block(n)%x(0:xdim)

是数据指针赋值之一。正如其他答案所提到的那样, bounds重映射这样的事情。也可以给出边界规范。

这两个案例都没有发生在这里,我将在稍后讨论,所以我们就是这样(见Fortran 2008,7.2.2.3)

  

如果出现 bounds-spec-list ,则指定下限;否则,每个维度的下限是应用于指针目标的相应维度的内在函数LBOUND(13.7.90)的结果。

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

  

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

这意味着LBOUND(block(n)%x)在这种情况下的结果为0,但LBOUND(block(n)%x(0:xdim))始终为结果1

这意味着什么

x => block(n)%x  ! x has lower bound 0
x => block(n)%x(0:xdim)  ! x has lower bound 1

现在,弗拉基米尔F提到重新映射:

x(0:xdim) => block(n)%x(0:xdim)

这表示x具有请求的上限和下限。这是非常合理的,但附带了几个警告:

  • 必须小心重复边界;左手大小的元素数量必须与右手大小相同,如果少,那么它就是一个较小的数组。
  • 右侧必须是rank-1或者只是连续的。

所有这些条件都成立,但这需要关注。

最后,有一个边界规范:

x(0:) => block(n)%x(0:xdim)

这仍然指定下限,但指针和目标总是具有相同的大小,并且对等级/邻接没有限制。

总结:使用x => block(n)%x来解决这个问题的简单情况。

答案 1 :(得分:1)

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

例如:

> 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

编译器会直接告诉您错误的位置。数组x从元素1开始,您正在尝试访问元素0,因此您已超出范围。它出现在52行,即

   write(*,*) "i= ",i, "j= ",j, block(i)%x(j), x(j)

为什么会这样?因为

  x => block(n)%x(0:xdim)

使x指向block(n)%x(0:xdim),但x将从1开始!

在Fortran 2003中,您可以执行此操作(指针重新映射):

 x(0:xdim) => block(n)%x(0:xdim)

并且按预期工作,但最好只使用block(n)%x