【问题标题】:Intel Fortran error "allocatable array or pointer is not allocated"Intel Fortran 错误“未分配可分配数组或指针”
【发布时间】:2014-10-09 15:39:30
【问题描述】:

当我尝试运行一个巨大的 Fortran 代码(代码使用 Intel 编译器版本 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's documentation

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

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

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

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

    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:严重(153)”错误会在生成新错误时消失......但是,我仍然认为这个语句本身不会出错......它就像它一样只是忽略 if... 条件并直接读取 deallocate 指令,这对我来说似乎很奇怪。

【问题讨论】:

  • “但是,我看不到错误与我添加到代码中的“写语句”之间有任何关系。” 正如其他人指出的那样,确实没有这两者之间的关系。无论如何,应该帮助您确定错误发生的位置是将-traceback 标志添加到您的编译器选项中。然后您应该能够立即知道您正在释放的变量尚未分配。然后你应该找到它应该被分配的地方,并确定为什么没有分配(也请参阅 Ettiene 的评论)。
  • 可能StrArray的关联状态未定义?
  • 稍微放大@francescalus 的评论——这是 Fortran 指针的一个特性,当它们第一次声明(没有初始化)时,它们既不是associated,也不是disassociated,而是undefined。将未定义的指针传递给 associated 内在函数会产生未定义的行为。
  • 那我怎样才能避免这种说法但仍然保持它的意思呢? =)
  • 为了避免未定义的指针在声明时初始化(可能是=> null())。或者全力以赴,避免使用指针并使用可分配数组——这可能会将我们带入一个新问题,而不是 cmets 中的这种闲聊。

标签: fortran intel-fortran


【解决方案1】:

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

【讨论】:

  • 从编译器提供给 OP 的错误消息中,我建议寻找任何可能无法达到的分配语句(取决于条件)。您可能正在释放或分配未分配的数组。事实上,write 语句与错误无关,它只是更改了未分配内存时写入的位置,现在它发生在不会造成损坏的位置。但这不能是永久修复,因为错误可能会或可能不会再次出现,具体取决于运行代码时机器的状态。
  • 非常感谢以上建议。我确实有一个问题,如何应用“-traceback”编译器选项?在代码的主要部分,它使用了 -traceback 以及调试标志中的其他选项。而在代码的其他一些部分,没有这样的调试标志。所以,我想知道一般如何使用这个编译器选项?我试过这样的“make -f Makefile -d -traceback”,但它不起作用......
  • 更新:我刚刚想出了在其中添加回溯的方法:make "FLAGS = -trackback"...谢谢。
【解决方案2】:

听起来有些早期的 cmets 给出了一般性的解释。不过,

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 Allcoat()。

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

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

一个可能的解决方案是循环读取文件中的每一行的第一个字符或以某种方式前进。让循环跟踪读取的每一行的“总和”,直到 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 位之前知道大小等(即在 FileRead s/r 调用之前设置文件大小)。

但是,这仍然给您留下了使用什么 Character(Len) 的问题。对此有许多可能的解决方案。其中三个是:

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

这对

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

这是 2-D,因为每行中的字符需要 1 D,行中需要 2nd 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 等的“内务管理”,您需要实施)。

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

【讨论】:

    猜你喜欢
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 2012-03-11
    • 2016-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多