英特尔 Fortran 错误"allocatable array or pointer is not allocated"



当我试图运行一个巨大的Fortran代码(代码是使用英特尔编译器版本13.1.3.192编译的),它给了我这样的错误信息:

... 
Info[FDFI_Setup]: HPDF code version number is  1.00246
forrtl: severe (153): allocatable array or pointer is not allocated
Image              PC                Routine            Line        Source
arts               0000000002AD96BE  Unknown               Unknown  Unknown
arts               0000000002AD8156  Unknown               Unknown  Unknown
arts               0000000002A87532  Unknown               Unknown  Unknown
...
尽管如此,如果我在子例程中插入一个小的写语句(只是检查代码,而不是干扰代码的原始目的),如下所示(我不能把所有的代码都放进去,因为它们太大了):
    ...
    endif
    call GetInputLine(Unit,line,eof,err)
  enddo
  if(err) return
  ! - [elfsummer] 20140815 Checkpoint 23
  open(unit = 1, file = '/bin/monitor/log_checkpoint',status='old',position='append')
  write(1,*) "BEFORE checking required keys: so far so good!"
  close(1)
  ! check required keys
  ! for modes = 2,3, P and T are the required keys
  if(StrmDat%ModeCI==2.or.StrmDat%ModeCI==3) then
  ...
然后,上面显示的错误信息突然消失,代码可以正常运行了!我还试图在源代码的其他位置插入这样的写语句,但上述错误信息仍然存在。

根据Intel的文档:

severe(153):未分配可分配数组或指针IOS_INVDEALLOC美元。一个Fortran 90可分配的数组或指针在你试图释放它的时候必须已经被分配了。必须先分配数组或指针,然后才能再次释放它。注意:这个错误可以在DEALLOCATE语句中由STAT返回。

然而,我看不出错误和我添加到代码中的"write statements"之间的任何关系。在我添加写语句的位置没有这样的"分配"命令。

所以我很困惑。有人知道原因吗?任何帮助都非常感谢!!

使用traceback选项,我可以直接定位错误源:

    subroutine StringRead(Str,delimiter,StrArray,ns)   ! [private] read strings separated by    delimiter
    implicit none
    character*(*),intent(in)    :: Str
    character*(*),intent(in)    :: delimiter
    character*(*),pointer       :: StrArray(:)
    integer,intent(out)         :: ns
! - local variables
    character(len=len(Str))     :: tline
    integer                     :: nvalue,nvalue_max
    character(len=len(StrArray)),pointer:: sarray(:),sarray_bak(:)
    integer                     :: len_a,len_d,i
    ! deallocate StrArray
    if(associated(StrArray)) deallocate(StrArray)

根据回溯给我的信息,错误在上面显示的最后一个语句中。如果我注释掉这条语句,那么"fortl: severe(153)"错误就会消失,同时生成新的错误…但是,我仍然认为这句话本身不会出错……它表现得好像忽略了if…条件并直接读取deallocate推荐,这对我来说似乎很奇怪。

可能存在非法写入内存并破坏存储分配信息的结构的错误。更改代码可能会导致内存损坏发生在其他地方,并且该特定错误会消失。一般来说,非法内存访问在Fortran中通常发生两种情况:1)非法下标,2)实际参数和虚拟参数不匹配,即调用中的变量和过程中声明的变量不匹配。您可以使用编译器的运行时下标检查选项来搜索第一种类型的错误。您可以通过将所有过程放在模块中并对这些模块进行use来防止第二种情况,以便编译器可以检查参数的一致性。

听起来前面的一些评论给出了一般的解释。然而,

1) StrArray(:)是Intent(out)吗?也就是说,您是否将文件的行读入s/r中的StrArray(),并希望将其作为文件的内容返回?如果是,则将其声明为(Out)或其他类型。

2)为什么StrArray()是指针?它需要是一个指针吗?如果您想要的只是文件内容,那么最好使用非指针。

你可能仍然需要一个Allocatable,或者Automatic之类的,但是在很多情况下,非指针更容易。

3)如果你必须有StrArray(:)作为指针,那么它的大小/形状等必须在使用之前创建。如果调用序列ACTUAL Arg是正确定义的(如果StrArray()是Intent(In)或Intent(InOUT),那么可能会这样做。

相反,如果它是(Out),那么,与所有指针数组一样,它必须是s/r中的FIRST Allcoated()。

如果它没有在早期的某个地方分配,那么它是未定义的,因此DeAllocate()失败,因为它没有什么可DeAlloc的,因此Stat = 153。

4)您可能希望在不首先知道要读取的行数的情况下使用此方法读取文件。在这种情况下,您不能(至少不容易)提前分配StrArray(),因为您不知道Size。在这种情况下,需要其他策略。

一个可能的解决方案是一个循环,简单地读取文件中每一行的第一个字符,或者以某种方式前进。让循环跟踪每行读取的"sum",直到EOF。然后,您将知道文件的大小(以行数为单位),然后分配StrArray(SumLines)或其他东西。就像

SumLines = 0
Do i=1, ?? (or use a While)
    ... test to see if "line i" exists, or EOF, if so, Exit
    SumLines = SumLines + 1
End Do

最好在单独的s/r中执行此操作,以便在调用FileRead位之前知道Size等(即在FileRead s/r调用之前设置文件大小)。

然而,这仍然给您留下了使用哪个字符(Len)的问题。有许多可能的解决方案。其中三个是:

a)使用最大长度,如Character(Len = 2048), Intent(Out),或者更好的是,一些编译时常量参数,称为MaxLineWidth

这对于<= MaxLineWidth的行有明显的限制,并且当有许多"短行"等时,内存使用量可能会过大。

b)使用单个字符数组,如Character(Len = 1), Intent(Out):: StrArrayChar(:,:)

这是2-D,因为您需要用1个D表示每行中的字符,用第二个D表示每行。

这比a)好一点,因为它可以控制行宽。

c)更通用的方法可能依赖于用户定义类型,例如:
Type MyFileType
    Character(Len=1), Allocatable   :: FileLine(:)  ! this give variable length lines, but each "line" must be allocated to the length of the line
End Type MyFileType

然后,创建一个这种类型的数组,例如:

Type(MyFileType), Allocatable   :: MyFile(:)    ! or, instead of Allocatable, can use Automatic etc etc

然后,将MyFile分配到Size = num行

…无论如何,有各种各样的选择,每个都有自己的适合于不同的情况(我已经省略了许多关于deallos等的"管家",您将需要实现)。

顺便说一下,对于许多不明确支持"可变长度字符串"的Fortran编译器来说,c)也是一个可能的原型。

相关内容

最新更新