在fortran中更改数组维数
在Fortran 90/95中,基本上有两种方法可以将数组传递给子例程:在fortran中更改数组维数,fortran,Fortran,在Fortran 90/95中,基本上有两种方法可以将数组传递给子例程: PROGRAM ARRAY INTEGER, ALLOCATABLE :: A(:,:) INTEGER :: N ALLOCATE(A(N,N)) CALL ARRAY_EXPLICIT(A,N) ! or CALL ARRAY_ASSUMED(A) END PROGRAM ARRAY SUBROUTINE ARRAY_EXPLICIT(A,N) INTEGER :: N INTEGER :: A(N,N) ! bla
PROGRAM ARRAY
INTEGER, ALLOCATABLE :: A(:,:)
INTEGER :: N
ALLOCATE(A(N,N))
CALL ARRAY_EXPLICIT(A,N)
! or
CALL ARRAY_ASSUMED(A)
END PROGRAM ARRAY
SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N,N)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT
SUBROUTINE ARRAY_ASSUMED(A)
INTEGER, ALLOCATABLE :: A(:,:)
N=SIZE(A,1)
! bla bla
END SUBROUTINE ARRAY_ASSUMED
通常通过使用模块,第二个需要显式接口
在FORTRAN77中,我习惯了第一种选择,我读到如果你通过整个数组,这也是最有效的
显式形状的好处在于,我还可以调用一个子例程,并将数组视为向量而不是矩阵:
SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N**2)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT
我想知道是否有一种很好的方法可以使用第二个假定的形状界面来完成这类工作,而不必复制它。请参见重塑的内在特性,例如 或者,如果您希望避免复制(在某些情况下,优化编译器可能能够在不复制的情况下进行重塑,例如,如果之后不使用RHS数组,但我不会指望它),从Fortran 2003开始,您可以使用边界重映射将指针分配给不同级别的目标。比如说
program ptrtest
real, pointer :: a(:)
real, pointer :: b(:,:)
integer :: n = 10
allocate(a(n**2))
a = 42
b (1:n, 1:n) => a
end program ptrtest
@janneb已经回答了“重塑”。重塑是一个函数——通常在赋值语句中使用,因此将有一个复制操作。也许可以不用使用指针进行复制。除非阵列很大,否则最好使用重塑 就运行时间而言,我怀疑显式形状数组是否比假定形状更有效。我倾向于使用Fortran>=90语言的特性,并使用假定的形状声明。。。这样你就不必费心去传递维度了 编辑: 我用ifort 11、gfortran 4.5和gfortran 4.6测试了@janneb的示例程序。在这三个版本中,它只适用于gfortran 4.6。有趣的是,要将一维数组连接到现有的二维数组,则需要Fortran 2008的另一个新功能,即“连续”属性——至少根据gfortran 4.6.0 20110318。如果声明中没有此属性,则会出现编译时错误
program test_ptrs
implicit none
integer :: i, j
real, dimension (:,:), pointer, contiguous :: array_twod
real, dimension (:), pointer :: array_oned
allocate ( array_twod (2,2) )
do i=1,2
do j=1,2
array_twod (i,j) = i*j
end do
end do
array_oned (1:4) => array_twod
write (*, *) array_oned
stop
end program test_ptrs
您可以使用假定大小的数组,但它可能意味着多层包装器 例行程序:
program test
implicit none
integer :: test_array(10,2)
test_array(:,1) = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)
test_array(:,2) = (/11, 12, 13, 14, 15, 16, 17, 18, 19, 20/)
write(*,*) "Original array:"
call print_a(test_array)
write(*,*) "Reshaped array:"
call print_reshaped(test_array, size(test_array))
contains
subroutine print_reshaped(a, n)
integer, intent(in) :: a(*)
integer, intent(in) :: n
call print_two_dim(a, 2, n/2)
end subroutine
subroutine print_two_dim(a, n1, n2)
integer, intent(in) :: a(1:n1,1:*)
integer, intent(in) :: n1, n2
call print_a(a(1:n1,1:n2))
end subroutine
subroutine print_a(a)
integer, intent(in) :: a(:,:)
integer :: i
write(*,*) "shape:", shape(a)
do i = 1, size(a(1,:))
write(*,*) a(:,i)
end do
end subroutine
end program test
我也希望做同样的事情,但遇到了这次讨论。没有一种解决方案适合我的目的,但我发现,如果您使用的是fortran 2003标准(当前fortran 90/95编译器倾向于支持),那么有一种方法可以在不使用iso_c_绑定复制数据的情况下重塑数组。我知道这个讨论由来已久,但我想我应该补充一下我的想法,以便其他人了解这个问题 关键是使用函数C_LOC将数组转换为数组指针,然后使用C_F_指针将其转换回具有所需形状的fortran数组指针。使用C_-LOC的一个挑战是,C_-LOC仅适用于具有直接指定形状的数组。这是因为fortran中具有不完整大小规范的数组(即,对于某些维度使用:的数组)包括一个数组描述符以及数组数据。C_LOC不提供数组数据的内存位置,而是提供描述符的位置。因此,可分配数组或指针数组不能与C_LOC一起使用(除非您需要编译器特定的数组描述符数据结构的位置)。解决方案是创建一个子例程或函数,将数组作为固定大小的数组接收(大小实际上并不重要)。这会导致函数(或子例程)中的数组变量指向数组数据的位置,而不是数组描述符的位置。然后使用C_LOC获取指向数组数据位置的指针,并使用C_F_指针将该指针转换回具有所需形状的数组。所需的形状必须传递到此函数中,才能与C_F_指针一起使用。以下是一个例子:
program arrayresize
implicit none
integer, allocatable :: array1(:)
integer, pointer :: array2(:,:)
! allocate and initialize array1
allocate(array1(6))
array1 = (/1,2,3,4,5,6/)
! This starts out initialized to 2
print *, 'array1(2) = ', array1(2)
! Point array2 to same data as array1. The shape of array2
! is passed in as an array of intergers because C_F_POINTER
! uses and array of intergers as a SIZE parameter.
array2 => getArray(array1, (/2,3/))
! Change the value at array2(2,1) (same as array1(2))
array2(2,1) = 5
! Show that data in array1(2) was modified by changing
! array2(2,1)
print *, 'array(2,1) = array1(2) = ', array1(2)
contains
function getArray(array, shape_) result(aptr)
use iso_c_binding, only: C_LOC, C_F_POINTER
! Pass in the array as an array of fixed size so that there
! is no array descriptor associated with it. This means we
! can get a pointer to the location of the data using C_LOC
integer, target :: array(1)
integer :: shape_(:)
integer, pointer :: aptr(:,:)
! Use C_LOC to get the start location of the array data, and
! use C_F_POINTER to turn this into a fortran pointer (aptr).
! Note that we need to specify the shape of the pointer using an
! integer array.
call C_F_POINTER(C_LOC(array), aptr, shape_)
end function
end program
我正在使用ifort 14.0.3和2D到1D转换,我可以为2D数组使用可分配数组,为1D使用指针数组:
integer,allocatable,target :: A(:,:)
integer,pointer :: AP(:)
allocate(A(3,N))
AP(1:3*N) => A
正如@M.S.B所提到的,如果A和AP都有指针属性,我必须使用A的连续属性来保证转换的一致性。Gfortran对接口有点偏执。它不仅想知道参数的类型、种类、等级和数量,还想知道形状、目标属性和意图(尽管我同意意图部分)。我遇到了类似的问题 对于gfortran,有三种不同的维度定义:
1.固定
2.变量
3.假定大小 使用ifort,类别1和类别2被认为是相同的,因此您可以在界面中将任何标注大小定义为0,这样就可以了
program test
implicit none
integer, dimension(:), allocatable :: ownlist
interface
subroutine blueprint(sz,arr)
integer, intent(in) :: sz
integer, dimension(0), intent(in) :: arr
! This zero means that the size does not matter,
! as long as it is a one-dimensional integer array.
end subroutine blueprint
end interface
procedure(blueprint), pointer :: ptr
allocate(ownlist(3))
ownlist = (/3,4,5/)
ptr => rout1
call ptr(3,ownlist)
deallocate(ownlist)
allocate(ownlist(0:10))
ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
ptr => rout2
call ptr(3,ownlist)
deallocate(ownlist)
contains
! This one has a dimension size as input.
subroutine rout1(sz,arr)
implicit none
integer, intent(in) :: sz
integer, dimension(sz), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout1
! This one has a fixed dimension size.
subroutine rout2(sz,arr)
implicit none
integer, intent(in) :: sz
integer, dimension(0:10), intent(in) :: arr
write(*,*) "Ignored integer: ",sz
write(*,*) arr
write(*,*) arr(1)
end subroutine rout2
end program test
Gfortran抱怨接口问题。将0更改为'sz'可以解决四个'rout1'的问题,但不能解决'rout2'的问题
但是,您可以愚弄gfortran,用dimension(0:10+0*sz)代替dimension(0:10),gfortran编译并给出相同的结果
结果是ifort
这是一个愚蠢的把戏,它依赖于可能不存在的整数'sz'的存在。另一个方案:
program difficult_test
implicit none
integer, dimension(:), allocatable :: ownlist
interface
subroutine blueprint(arr)
integer, dimension(0), intent(in) :: arr
end subroutine blueprint
end interface
procedure(blueprint), pointer :: ptr
allocate(ownlist(3))
ownlist = (/3,4,5/)
ptr => rout1
call ptr(ownlist)
deallocate(ownlist)
allocate(ownlist(0:10))
ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
ptr => rout2
call ptr(ownlist)
deallocate(ownlist)
contains
subroutine rout1(arr)
implicit none
integer, dimension(3), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout1
subroutine rout2(arr)
implicit none
integer, dimension(0:10), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout2
end program difficult_test
这在ifort下工作的原因与前面的示例相同,但是gfortran抱怨接口。我不知道怎样才能修好它
我唯一想告诉gfortran的是‘我还不知道尺寸大小,但我们会解决它。’。但是这需要一个备用的整数参数(或者我们可以转换成整数的其他东西)来愚弄gfortran。“不复制它。”不复制什么?我认为@steabert意味着改变数组的形状,而不是将其复制到1D数组。thnx@M.S.B。为了澄清,这就是我的意思。所以不是下面提到的
重塑内在解决方案@M.S.B.我怀疑它。比重塑内在不是一个解决方案。嗯,我认为事情很大程度上取决于故事中的“!布拉,布拉”部分你总是可以使用循环遍历2D数组,就像它是1D一样。@kemiisto-true,你总是可以控制索引的方式,但我想知道这种“手动”处理是否是唯一的方法。我有一个子程序