Fortran程序输出疑问:为何输出4而非1?(GNU 6.4编译器)
问题分析与解决
你的问题出在非连续数组段传递给指针形参的未定义行为上,我来一步步拆解:
为什么输出是4而不是1?
先理清你的数组存储和赋值逻辑:
- 主程序里
allocate(t(0:100,20))创建了一个第一维下标0-100、第二维1-20的数组,Fortran默认列优先存储——也就是说,内存里会先存完t(:,1)的所有元素(t(0,1), t(1,1), ..., t(100,1)),再存t(:,2),以此类推。 - 你给
t(i,j)赋值为i*j,所以t(1,1)=1,t(2,2)=4。
问题核心出在调用te(t(1:,:))的环节:
t(1:,:)是一个非连续的数组段:它包含t(1,1)到t(100,1)、t(1,2)到t(100,2)……每一列内部的元素是连续的,但列与列之间隔着t(0,j),所以整个数组段在内存中是不连续的。- Fortran标准明确规定:不能将非连续的数组表达式(非指针/非可分配数组)传递给指针形参,这种情况属于未定义行为——编译器可以自由处理,而GNU 6.4在这里的处理逻辑出错,导致指针
a错误关联到了内存中t(2,2)的位置,最终输出4。
如何修复?
有两种简单可靠的修复方式:
1. 将子routine的形参改为非指针的假定形状数组
把te的形参声明里的pointer属性去掉,改成普通假定形状数组:
subroutine te(a) implicit none real,dimension(:,:),intent(in) :: a ! 移除pointer属性 print *, a(1,1) end subroutine te
这样编译器会自动处理非连续数组段(哪怕需要创建临时数组),就能正确输出1。
2. 先复制到连续临时数组再传递
如果必须保留指针形参,可以先把非连续的数组段复制到一个连续的临时指针数组,再传递给te:
program aa implicit none real, pointer, dimension(:,:) :: t => null(), temp => null() integer :: i,j allocate(t(0:100,20)) do i = 0, 100 do j = 1, 20 t(i,j) = i*j end do end do ! 创建临时连续数组并复制内容 allocate(temp(1:100,20)) temp = t(1:,:) call te(temp) deallocate(temp) stop contains subroutine te(a) implicit none real,dimension(:,:),pointer,intent(in) :: a print *, a(1,1) end subroutine te end program aa
这种方式也能稳定输出1。
内容的提问来源于stack exchange,提问作者saqima
相关产品推荐
相关产品推荐

