我想扩展结构的可分配属性,MOVE_ALLOC()
似乎是最干净的方法。因此,我使用指向结构作为参数的MOVE_ALLOC()
创建了一个例程,并尝试调用:
Pointer, Intent(in)
其中Type(STRUCT1), Pointer, Intent(in) :: str1
...
call MOVE_ALLOC(TO= str1%arrayofint , FROM= temparray)
是指向结构的指针,str1
是要扩展的属性。请参见以下示例代码的子例程arrayofint
。 编译器ifort返回以下错误:
LOC_extendsecond()
好像Source1.f90(91): error #7999: The FROM or TO arguments of a MOVE_ALLOC reference must not be INTENT(IN). [MOVE_ALLOC]
是str1%arrayofint
。 要注意指针Intent(in)
是str1
,指向结构Intent(in)
的属性是否应视为str1%arrayofint
?
为了调查此问题,我尝试不使用Intent(in)
,发现在例程中取消分配或分配MOVE_AllOC()
不会触发ifort的任何错误或警告。请参见示例代码的子例程str1%arrayofint
。
[我的一个同事提出了一种解决方法(感谢Luc!):创建了指针的本地副本,并且可以使用该指针的本地副本调用LOC_extendfirst()
,而不会因ifort引发错误。请参见示例代码的子例程MOVE_AllOC()
。
LOC_extendthird()
这里是示例代码:
Type(STRUCT1), Pointer, Intent(in) :: str1
Type(STRUCT1), Pointer :: str2
str2=>str1
...
call MOVE_ALLOC(TO= str2%arrayofint , FROM= temparray)
在Windows上使用Module Source1
Implicit None
Public :: STRUCT1
Private
Type STRUCT1
Integer, dimension(:), allocatable :: arrayofint
End Type STRUCT1
Contains
Subroutine newstruct1(str1,ier,errmsg)
Type(STRUCT1), Pointer, Intent(inout) :: str1
Integer, Intent(out) :: ier
character(len=256), Intent(out) :: errmsg
ier=0
allocate(str1,stat=ier, errmsg=errmsg)
if( ier>0) return
allocate(str1%arrayofint(2),stat=ier, errmsg=errmsg)
if( ier>0) return
End Subroutine newstruct1
Subroutine LOC_extendfirst(str1,targetsize,ier,errmsg)
Type(STRUCT1), Pointer, Intent(in) :: str1
Integer, Intent(out) :: ier
character(len=256), Intent(out) :: errmsg
Integer, Intent(in) :: targetsize
Integer,dimension(1) :: shp
Integer :: newsize , formersize
Integer, dimension(:), allocatable :: temparray
ier=0
shp=shape(str1%arrayofint)
formersize=shp(1)
if (targetsize .GT. formersize) then
newsize=MAX(targetsize,2*formersize)
allocate(temparray(newsize),stat=ier, errmsg=errmsg)
if( ier>0) then; return ; endif
temparray(1:formersize)=str1%arrayofint
allocate(temparray(formersize),stat=ier, errmsg=errmsg)
if( ier>0) then; return ; endif
temparray=str1%arrayofint
if(allocated(str1%arrayofint)) deallocate(str1%arrayofint)
allocate(str1%arrayofint(newsize),stat=ier, errmsg=errmsg)
if( ier>0) then; return ; endif
str1%arrayofint(1:formersize)=temparray
if(allocated(temparray)) deallocate(temparray)
endif
End Subroutine LOC_extendfirst
Subroutine LOC_extendsecond(str1,targetsize,ier,errmsg)
Type(STRUCT1), Pointer, Intent(in) :: str1
Integer, Intent(out) :: ier
character(len=256), Intent(out) :: errmsg
Integer, Intent(in) :: targetsize
Integer,dimension(1) :: shp
Integer :: newsize , formersize
Integer, dimension(:), allocatable :: temparray
ier=0
shp=shape(str1%arrayofint)
formersize=shp(1)
if (targetsize .GT. formersize) then
newsize=MAX(targetsize,2*formersize)
allocate(temparray(newsize),stat=ier, errmsg=errmsg)
if( ier>0) then; return ; endif
temparray(1:formersize)=str1%arrayofint
! TODO uncomment the following line to get error from ifort
call MOVE_ALLOC(TO= str1%arrayofint , FROM= temparray)
endif
End Subroutine LOC_extendsecond
Subroutine LOC_extendthird(str1,targetsize,ier,errmsg)
Type(STRUCT1), Pointer, Intent(in) :: str1
Integer, Intent(out) :: ier
character(len=256), Intent(out) :: errmsg
Integer, Intent(in) :: targetsize
Integer,dimension(1) :: shp
Integer :: newsize , formersize
Integer, dimension(:), allocatable :: temparray
Type(STRUCT1), Pointer :: str2
ier=0
str2=>str1
shp=shape(str2%arrayofint)
formersize=shp(1)
if (targetsize .GT. formersize) then
newsize=MAX(targetsize,2*formersize)
allocate(temparray(newsize),stat=ier, errmsg=errmsg)
if( ier>0) then; return ; endif
temparray(1:formersize)=str1%arrayofint
call MOVE_ALLOC(TO= str2%arrayofint , FROM= temparray)
endif
End Subroutine LOC_extendthird
End Module Source1
调用ifort 19.0.2.190 IA32会产生错误。
相反,在使用ifort /nologo /debug:full /Od /debug-parameters:all /warn:unused /warn:truncated_source /warn:uncalled /warn:interfaces /Qsave /traceback /check:pointer /check:bounds /check:uninit /libs:static /threads /dbglibs /c /Qm32 "Source1.f90"
的Debian上使用gcc 6.3.0中的gfortran不会导致任何错误:它仅显示有关未使用功能的警告。
关于Fortran,我是一个新手。我知道Fortran指针比C指针包装更多的数据,所以我想知道修改属性gfortran -c Source1.f90 -Wall
是否正确,因为str1%arrayofint
是str1
,就像在函数中修改str1-> arrayofint正确一样,是指针str1通过值传递。
如何解决ifort和gfortran之间的行为差异? ifort在当前情况下报告错误,是否正确?为什么ifort认为Pointer, Intent(in)
是str1%arrayofint
,就像Intent(in)
是str1
而不是Type(STRUCT1), Intent(in)
一样?如Type(STRUCT1), Pointer, Intent(in)
所示,引入指针的本地副本是缓解此问题的最合适方法吗?
对于指针虚拟参数,LOC_extendthird()
属性表示指针不应出现在所谓的指针关联上下文中。松散地,这意味着不允许您(潜在地)更改哑元参数的指针关联。您可以更改此指针的目标值。
对于指针虚拟参数,intent(in)
属性不会“级联”到参数的子对象(例如,在本例中为组件intent(in)
):arrayofint
目标的组件arrayofint
不会具有str1
属性。
在类似intent(in)
的引用中带有str1%arrayofint
指针,这是对str1
的target的分量arrayofint
的引用。即使str1
属性确实适用于指针伪参数的子对象,由intent(in)
引用的对象也不是str1%arrayofint
的子对象。
[认为这样的对象具有str1
属性是错误的。您已经找到了解决ifort中此类缺点的有效方法。您应该考虑将此漏洞报告给英特尔。
最后,也许有更好的方法来解决不使用指针虚拟参数而调整组件大小的问题,但我不会在此答案中考虑这些。