代码之家  ›  专栏  ›  技术社区  ›  elfsummer

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

  •  1
  • elfsummer  · 技术社区  · 10 年前

    当我试图运行一个庞大的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
      ...
    

    然后,上面显示的错误消息突然消失,代码可以正确运行!我还尝试在源代码的其他位置插入这样的write语句,但上面的错误消息仍然存在。

    根据 Intel's documentation :

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

    然而,我看不到错误与我添加到代码中的“write语句”之间有任何关系。在我添加write语句的位置没有这样的“allocate”命令。

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

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

        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…条件,直接读取了解除分配的赞扬,这在我看来很奇怪。

    2 回复  |  直到 7 年前
        1
  •  2
  •   M. S. B.    10 年前

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

        2
  •  0
  •   DrOli    10 年前

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

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

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

    您可能仍然需要一个Allocable或Automatic等,但在很多情况下,非指针更容易。

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

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

    如果早期某个地方没有分配,那么它是未定义的,因此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调用之前设置文件大小)。

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

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

    这对<=MaxLineWidth,当有许多“短线”等时,内存使用量可能会过大。

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

    这是二维的,因为每行中的字符需要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行

    ……无论如何,有多种选择,每种选择都适合不同的环境(我省略了很多关于DeAlloc等的“内务管理”,您需要实现这些)。

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