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

数组的分段错误,但仅当派生类型的组件

  •  5
  • JohnE  · 技术社区  · 6 年前

    非常简单的设置,在linux上使用gfortran 4.8.5(red hat):

    • 如果real数组(在派生类型中)的大小为2000000,则会得到segfault。这似乎是一个标准的堆栈/堆问题,因为如果我检查 ulimit

    • 如果数组是 不是 在派生类型内部

    • = 0.0 解决问题

    编辑以添加: 注意,我已经发布了一个后续问题 Segmentation fault related to component of derived type 这代表了一个更现实的用例,并进一步缩小了出现这种情况的条件。

    program main
    
        call sub1     ! seg fault  if col size >   2,100,000
        call sub2     ! works fine at col size = 100,000,000  
    
    end program main
    
    subroutine sub1
    
        type table
            real :: col(2100000) = 0.0     ! works if "= 0.0" removed
        end type table
    
        type(table) :: table1
        table1%col = 1.0
    
    end subroutine sub1
    
    subroutine sub2
        real :: col(100000000) = 0.0
        col = 1.0
    end subroutine sub2
    

    • 这是预期的行为,还是在较新版本的gfortran中修复的错误?

    • 建议如何避免这种情况(请假设我近期无法更新到gfortran的新版本)?我几乎肯定会用一个可分配的数组组件来解决这个问题,原因不是特定的,但这可能不是一个理想的通用解决方案,我想知道我在这里有哪些好的选择。

    • 特别是,初始化派生类型的组件是否是一种错误的做法?

    2 回复  |  直到 6 年前
        1
  •  6
  •   jme52    6 年前

    这可能是由于堆栈不足而导致的运行时问题,而不是gfortran的错误。

    Gfortran使用堆栈存储自动数组和其他初始化数据。当一个这样的数组很小时,代码不会产生问题,但当数组的大小增加时会出现segfaults时,一个可能的原因是堆栈用完了。

    这个问题在最近版本的gfortran中似乎是一样的。我用gfortran 4.8.4、4.9.3、5.5.0、6.4.0、7.3.0和8.2.0编译并运行了您的程序。在所有情况下,我都获得了默认堆栈大小的分段错误,但堆栈大小稍微增加时没有错误。

    $  ./sfa
    Segmentation fault
    $ ulimit -s
    8192
    $ ulimit -s 8256 
    $ ./sfa && echo "DONE"
    DONE
    

    你的问题可以通过跑步来解决

    $ ulimit -s unlimited
    

    在执行二进制文件之前。我不知道这样做会有什么特别的惩罚,但是更了解内存管理细节的程序员,比如编译器开发人员,可能会有不同的想法。

    以上代码符合标准。正如注释中所解释的,缺少子例程的显式接口不是好的实践,但对于这些简单的子例程来说,这并不违反规则。

    最后,请注意,如果您使用OpenMP,上面的ulimit命令只影响主线程-您需要通过环境变量设置其他每个线程的堆栈大小 OMP_STACKSIZE ,不能是 unlimited

        2
  •  0
  •   JohnE    6 年前

    这些不一定是有用的解决方案,但以下是seg故障消失的一些条件。有几个人提到了缺少显式接口(虽然技术上不正确,但这是一个坏习惯),这似乎是其中的一个关键,因为代码的这两个更改中的任何一个都消除了seg错误,尽管这并不是那么简单,我将解释:

    1. 放置类型定义 table 在一个模块中

    让我简单地谈谈2。简单地以OP中的示例为例,然后通过将子例程放在模块中给它一个显式接口 没有

    program main
    
        use table_mod
    
        type(table) :: table1
    
        table1%col = 1.0
    
    end program main