英特尔Fortran错误“可分配的数组或指针未分配”

时间:2014-08-15 21:10:33

标签: fortran intel-fortran

当我尝试运行巨大的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
...

尽管如此,如果我在下面的一个子程序中插入一个小的write语句(只是为了检查代码,而不是打扰代码的最初目的)(我无法将所有代码放入,因为它们太大了):

    ...
    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's documentation

  

严重(153):未分配可分配的数组或指针   对于$ IOS_INVDEALLOC。当您尝试释放Fortran 90可分配数组或指针时,必须已分配它。您必须先分配数组或指针才能再次释放它。   注意:STAT可以在DEALLOCATE语句中返回此错误。

但是,我没有看到错误和我添加到代码中的“写入语句”之间的任何关系。在我添加写语句的位置没有这样的“allocate”命令。

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

使用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)

根据追溯给我的信息,错误在于上面显示的最后一个语句。如果我注释掉这个语句,那么“forrtl:severe(153)”错误会在生成新错误时消失......但是,我仍然不认为这个语句本身可能会出错......它就好像它一样只是忽略if ...条件并直接读取deallocate表示,这对我来说似乎很奇怪。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

听起来有些早期的评论给出了一般解释。但是,

1)StrArray(:)是一个Intent(out)吗?也就是说,您是否正在将文件的行读入s / r中的StrArray(),希望将其作为文件的内容返回?如果是这样,请将其声明为(Out)或其应该是什么。

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

您可能仍需要Allocatable或Automatic或其他内容,但在许多情况下非指针更容易。

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

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

如果早期没有在某处分配,那么它是未定义的,因此DeAllocate()失败,因为它对DeAlloc没有任何内容,因此Stat = 153。

4)您可能希望在不知道要读取的行数的情况下使用它来读取文件。在这种情况下,你不能(至少不容易),提前分配StrArray(),因为你不知道大小。在这种情况下,需要替代策略。

一种可能的解决方案是一个循环,它简单地读取文件中每一行的第一个char或以某种方式前进。让循环跟踪每行读取的“总和”,直到EOF。然后,您将知道文件的大小(以num行为单位),然后分配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调用之前设置文件大小)。

但是,这仍然会让您遇到使用Character(Len)的问题。有很多可能的解决方案。其中三个是:

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

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

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

这是2-D,因为每行中的字符需要1 D,而行的第2个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的数组,例如:

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

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

...无论如何,有各种各样的选择,每种选择都有适合不同情况的适应性(而且我省略了很多“管家”deAllocs等,你需要实施)。

顺便提一下,c)也是许多Fortran编译器的“可变长度字符串”的一个可能原型,它们不能明确支持。