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时,问题就开始了。在上面的输出中,最后两个值必须相等。那么问题出在哪里?
答案 0 :(得分:3)
Vladimir F's answer解释了问题的根源。但是,这个细节在这里的问题中经常出现,因此值得强调一些事情。
数组部分,即使它匹配数组的所有元素,也与整个数组不同。 block(n)%x
是一个完整的数组; block(n)%x(0:xdim)
是一个数组部分(即使该数组组件已绑定0
和xdim
)。
为什么这种差异很重要?我们来看一下指针赋值的规则。指针赋值语句
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
具有请求的上限和下限。这是非常合理的,但附带了几个警告:
所有这些条件都成立,但这需要关注。
最后,有一个边界规范:
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
。