传递带负步长的可分配character(:)切片致GFortran段错误,原因何在?
问题:为何以下Fortran程序会出现段错误?是编译器问题,还是违反了Fortran标准?
! gfortran -fcheck=all arrays10.f90 && ./a ! GNU Fortran (GCC) 11.4.0 program arrays10 implicit none ! 不可分配变量不会触发段错误。 !integer :: a(3) !character(5) :: b(3) integer, allocatable :: a(:) character(:), allocatable :: b(:) a = [1,2,3] b = [ character(5) :: "one", "two", "three"] print *, "Test integer array" print *, "loc: ", loc(a) call test01(a) call test01(a(size(a):1:-1)) print * print *, "Test character(*) (:)" print *, "loc: ", loc(b) call test02(b) ! 仅在负步长时触发段错误 call test02(b(size(b):1:-1)) print * print *, "Test character(*) (*)" print *, "loc: ", loc(b) call test03(b) ! 仅在负步长时触发段错误 call test03(b(size(b):1:-1)) print * contains subroutine test01(a) ! 无段错误 ! 要求连续内存,会创建临时数组 !integer, intent(in) :: a(3) ! 继承步长,不创建临时数组 integer, intent(in) :: a(:) integer i print *, "testing: ", rank(a), size(a) ! 但loc()显然有布局要求 ! 会创建临时数组 print *, loc(a) do i = 1, size(a) print *, a(i) end do print * end subroutine test01 subroutine test02(a) ! 触发以下错误: ! Segmentation fault - 无效内存引用 ! 强制可分配参数,无实用价值 !character(:), allocatable, intent(in) :: a(:) ! 这两种都应创建临时数组,但未看到警告。 ! 因为段错误会在(取消注释时)此处触发。 !character(*), intent(in) :: a(3) !character(5), intent(in) :: a(3) ! 不应创建临时数组。 character(*), intent(in) :: a(:) integer i print *, "testing: ", rank(a), size(a), len(a(1)) ! 再次强制创建临时数组会触发相同段错误 !print *, loc(a) ! 按原顺序迭代仍会触发段错误。 !do i = size(a), 1, -1 do i = 1, size(a) ! 在此处(第一次迭代)触发段错误:无效内存引用 print *, " ", a(i) end do print * end subroutine test02 subroutine test03(a) ! 结果与test02相同 ! 两种情况都会在此处触发段错误 character(*), intent(in) :: a(*) !character(*), intent(in) :: a(1) print *, "testing: ", rank(a), size(a(:3)), len(a(1)) print *, loc(a) print * end subroutine test03 end program arrays10
分析与解答
结论:这是gfortran编译器的bug,而非违反Fortran标准
现象总结
- 传递可分配字符数组的负步长切片(如
b(size(b):1:-1))给子例程时触发段错误; - 整数数组的相同操作无异常;
- 使用非可分配的字符数组时,负步长切片不会触发错误。
原因解释
- 标准合规性确认:你的代码完全符合Fortran标准。根据标准,传递非连续数组切片(负步长属于非连续)给假设形状(
a(:))或假设大小(a(*))的哑元是合法操作,编译器需正确处理(要么生成临时连续数组,要么通过数组描述符支持非连续访问)。 - gfortran特定缺陷:在GCC 11.4.0版本的gfortran中,存在针对可分配字符数组非连续切片的处理bug。当传递这类切片时,编译器生成的内存访问代码存在逻辑错误,导致程序尝试访问无效内存地址,进而触发段错误。
- 整数数组未出现问题,是因为gfortran对整数类型的非连续切片处理逻辑正确;
- 非可分配字符数组无问题,说明bug仅与可分配字符数组的切片内存管理相关。
验证与解决方案
- 升级编译器:尝试将gfortran升级到GCC 12或更高版本,这类字符数组切片的bug在后续版本中已被修复;
- 临时规避方案:在传递负步长切片前,手动创建临时连续数组(如
call test02([b(size(b):1:-1)])),强制编译器生成合法的连续内存副本。
内容的提问来源于stack exchange,提问作者user19087
相关产品推荐
相关产品推荐

