使用可分配的字符串作为可选参数时,我遇到分配失败。仅当我通过两个级别的过程调用时,才会出现此问题。在我的实际代码中, 调用get_level1() (如下所示)表示对列表数据结构的调用,而 调用get_level2() < / strong>表示在其记录之一上调用相同类型的访问器函数的列表。我已经精简了一个足以重现问题的示例。
在下面的代码中,当我调用 get_level2 时,直接通过可选参数返回了预期的字符串。当我调用 get_level1 时,依次调用 get_level2 会失败,对可选虚拟参数的分配失败。使用gdb,我发现分配尝试创建一个character * 1635 ...当它返回到实际参数时,显然是整数溢出,因为它认为分配是character * -283635612 ...
我的实际代码有许多可选参数,而不仅仅是一个。作为一个简单的示例,我添加了一个可选的整数参数。这次,我得到了一个空字符串,而不是分段错误。
在第二个示例中,整数参数有效,无论使用字符参数如何。 (我希望这是因为没有执行动态分配)整数的存在对字符没有影响。我还尝试将意图更改为(输入)。尽管我没有想到,但这不会改变行为。 [我相信 intent(out)会导致实际参数先释放,而 intent(inout)会保留实际参数的分配状态]
call get_level1( NUM=n ) ! works
call get_level1( NUM=n, TEXT=words ) ! fails
call get_level1( TEXT=words ) ! fails
我的编译cmd是:
gfortran -Wall -g -std=f2008ts stest1.f08 -o stest
环境
Linux 4.15.0-42-generic #45-Ubuntu SMP x86_64 GNU/Linux
GNU Fortran (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
带有一个可选参数的示例
module stest1
implicit none
character(:), allocatable :: data
contains
subroutine get_level2( TEXT )
implicit none
character(:), optional, allocatable, intent(out) :: TEXT
if ( PRESENT( TEXT ) ) then
TEXT = 'Prefix: ' // data // ' :postfix'
end if
end subroutine get_level2
subroutine get_level1( TEXT )
implicit none
character(:), optional, allocatable, intent(out) :: TEXT
call get_level2( TEXT )
end subroutine get_level1
end module stest1
program main
use stest1
implicit none
character(:), allocatable :: words
data = 'Hello Doctor'
call get_level1( words )
write(*,100) words
100 format( 'words = [',A,']' )
end program main
带有两个可选参数的示例
module stest2
implicit none
character(:), allocatable :: data
integer :: count
contains
subroutine get_level2( TEXT, NUM )
implicit none
character(:), optional, allocatable, intent(out) :: TEXT
integer, optional, intent(out) :: NUM
if ( PRESENT( TEXT ) ) then
TEXT = 'Prefix: ' // data // ' :postfix'
end if
if ( PRESENT( NUM ) ) then
NUM = count
end if
end subroutine get_level2
subroutine get_level1( TEXT, NUM )
implicit none
character(:), optional, allocatable, intent(out) :: TEXT
integer, optional, intent(out) :: NUM
call get_level2( NUM=NUM, TEXT=TEXT )
end subroutine get_level1
end module stest2
program main
use stest2
implicit none
character(:), allocatable :: words
integer :: n
count = 42
data = 'Hello Doctor'
call get_level1( TEXT=words )
write(*,100) words
write(*,110) n
100 format( 'words = [',A,']' )
110 format( 'N = [',I0,']' )
end program main
答案 0 :(得分:4)
您似乎遇到了编译器错误。我可以在gfortran 8.2.1上重现该问题:
Operating system error: Cannot allocate memory
Memory allocation failure in xrealloc
Error termination. Backtrace:
#0 0x7f9c0314f107 in write_character
at ../../../libgfortran/io/write.c:1399
#1 0x7f9c03153e66 in list_formatted_write_scalar
at ../../../libgfortran/io/write.c:1872
#2 0x400c78 in MAIN__
at /tmp/test.F90:43
#3 0x400cbe in main
at /tmp/test.F90:34
但是在5.1.1中,我看到了正确的输出:
Prefix: Hello Doctor :postfix
通过以下变通方法,我可以正常工作:
subroutine get_level1( TEXT )
implicit none
character(:), optional, allocatable, intent(out) :: TEXT
character(:), allocatable :: tmp
if ( PRESENT( TEXT ) ) then
call get_level2( tmp )
TEXT = tmp
else
call get_level2( )
endif
end subroutine get_level1
答案 1 :(得分:3)
这是编译器中的一个错误,在Windows上仍然停留在gfortran v9.0.0 (experimental)
中。您应该report it with the vendor。
我已经进行了一些测试,并且似乎仅在以下情况下失败:仅在以下情况下发生:将当前的可选参数作为对应于虚拟参数的实际参数传递给 { {1}}。前一句中的任何变体似乎都可以避免该错误并产生正确的结果。
我将您的示例简化为最小的测试用例:
character(:), allocatable, optional
program main
implicit none
character(:), allocatable :: txt
call sub1(txt)
print *, "main ", len(txt), txt ! prints: main 0 (or throws segfault)
contains
subroutine sub1(txt)
character(:), allocatable, optional :: txt
call sub2(txt)
print *, "sub1 ", len(txt), txt ! prints: sub1 0 (or throws segfault)
end
subroutine sub2(txt)
character(:), allocatable, optional :: txt
if(present(txt)) txt = "message"
print *, "sub2 ", len(txt), txt ! prints: sub2 7 message
end
end
内部的检查表明,该分配实际上在这里起作用。当将该假人与sub2
中的实际参数相关联时,似乎会发生问题。嗯...
同样,sub1
假人模式的任何变化都会在我的测试中产生正确的结果。因此,我建议您至少灵活运用先前的条件之一来规避越野车问题。有一些建议:
1。不可分配可选字符起作用,无论长度是固定的还是假定的长度;
这是一个带有固定长度变量和假定长度参数的示例。
优势:易于重构,破坏性/侵入性较小。
缺点:必须事先估计变量的长度,浪费存储空间。
character(:), allocatable, optional
2。。非可选,用于从program option1
implicit none
character(10) :: txt
call sub1(txt)
print *, "main ", len(txt), txt ! prints: main 10 message
contains
subroutine sub1(txt)
character(*), optional :: txt
call sub2(txt)
print *, "sub1 ", len(txt), txt ! prints: sub1 10 message
end
subroutine sub2(txt)
character(*), optional :: txt
if(present(txt)) txt = "message"
print *, "sub2 ", len(txt), txt ! prints: sub1 10 message
end
end
传递的实际参数,或者用于{{ 1}},也可以使其工作;
当然,如果可以重构代码来避免这种情况,那将是更好的解决方案。例如,您可以使用通用接口来实现类似的结果。或者,正如您在评论中所说,“ 在级别1使用局部变量并将所有可选参数传递给较低级别”。
缺点:可能需要更改较低级别过程的界面。
优点:如果它们是私有模块过程,则不会有问题;这是一个实现细节。
请考虑以下方法,该方法可以破解该错误并避免传递可选参数,因此请勿更改该过程的签名:
sub1
3。与任何其他类型 同样适用,无论其属性如何(即使是具有可分配字符组件的派生类型)。虽然,排名或种类的变化不计算在内。
我将向您展示涉及衍生类型的两个选项:一个具有可分配字符长度分量;另一个具有参数化派生类型。
优点:您可以保留代码结构以及所有可选内容。存储开销低。您甚至可以使用方法扩展DT并针对您的问题进行调整。
缺点:也许麻烦太多了。 PDT很酷,但它是gfortran中的新功能(且有错误)。
sub2
总结:您可以更改编译器或选择任何一种(或其他方式)来规避此错误并继续工作,直到编译器供应商解决该问题为止。