作为可选传递时截断延迟长度字符串
Truncation of deferred-length string when passing as optional
我在子例程之间传递可选的延迟长度字符串 (character(len=:), allocatable, optional
),并在 GCC 中出现意外行为。
这是我的最小示例,其中我通过接口例程将可选字符串传递给设置它的例程:
$ cat main.f90
module deepest_call_m
implicit none
contains
subroutine deepest_call(str)
character(len=:), allocatable, intent(OUT), optional :: str
if (present(str)) str = '12345'
write(*,*) 'at bot of deepest_call, str is "'//trim(str)//'"'
end subroutine deepest_call
end module deepest_call_m
module interface_call_m
implicit none
contains
subroutine interface_call(str)
use deepest_call_m, only : deepest_call
character(len=:), allocatable, intent(OUT), optional :: str
call deepest_call(str=str)
write(*,*) 'at bot of interface_call, str is "'//trim(str)//'"'
end subroutine interface_call
end module interface_call_m
program main
use interface_call_m, only : interface_call
implicit none
character(len=:), allocatable :: str
call interface_call(str=str)
write(*,*) 'at bot of main, str is "'//trim(str)//'"'
end program main
(请注意,为简单起见,我没有将写入语句包装在 if(present)
和 if(allocated)
中,尽管这对于实际实现是必要的。)
在 Intel 16.0 和 2019_U4 以及 PGI 15.10 中,此例程给出了预期的结果:str
在 deepest_call
中设置并且在 interface_call
和在 main
:
$ ifort --version
ifort (IFORT) 16.0.0 20150815
Copyright (C) 1985-2015 Intel Corporation. All rights reserved.
$ ifort main.f90 && ./a.out
at bot of deepest_call, str is "12345"
at bot of interface_call, str is "12345"
at bot of main, str is "12345"
但是,对于 gfortran 4.8.5:
$ gfortran --version
GNU Fortran (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36)
Copyright (C) 2015 Free Software Foundation, Inc.
GNU Fortran comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of GNU Fortran
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING
$ gfortran main.f90 && ./a.out
at bot of deepest_call, str is "12345"
at bot of interface_call, str is ""
at bot of main, str is ""
从 interface_call
中的 deepest_call
返回时字符串已被截断。对于 gfortran 7.3.0 和 8.2.0,如果没有提供编译时选项,代码会在运行时崩溃:
[chaud106@epyc-login-1-0 Testing]$ gfortran --version && gfortran main.f90 && ./a.out
GNU Fortran (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
at bot of deepest_call, str is "12345"
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
Backtrace for this error:
#0 0x2af39a82733f in ???
#1 0x400c68 in ???
#2 0x400d9c in ???
#3 0x400f0e in ???
#4 0x2af39a813494 in ???
#5 0x400878 in ???
#6 0xffffffffffffffff in ???
Segmentation fault
但是,添加编译时检查可以恢复以前的截断行为:
$ gfortran --version
GNU Fortran (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gfortran -g -fbacktrace -Wall -Wextra -std=f2008 -fcheck=all -Og main.f90 && ./a.out
at bot of deepest_call, str is "12345"
at bot of interface_call, str is ""
at bot of main, str is ""
这对我来说很像编译器错误,所以也许我只需要 post 在 bugzilla 上。但我想先在这里得到一些反馈,特别是:
我的测试用例是否符合标准?我对 deepest_call
的 str=str
参数特别好奇。当且仅当它在当前范围内是可选的时,我经常使用此结构将参数作为可选参数传递,但我无法在标准中轻松找到它,而且我不确定它是否真的有效。不过,仅传递 str
似乎会产生相同的行为。
是否有任何简单的解决方法?鉴于此问题影响范围广泛(4.8.5 到 8.2.0),简单地避免它们是不可行的。
是否有人知道其他版本的 gfortran 或其他编译器的这种行为?我只能轻松访问 GCC、Intel 和 PGI。
您的代码符合标准。
一个可选的伪参数在没有关键字的后续调用中可能是一个实际的伪参数,无论它是否存在。 (当然,只有在以下伪参数是可选的情况下才可能不存在。)即:
call interface_call(str)
与
一样正确
call interface_call(str=str)
至于(次优)解决方法(我看到 gfortran 9.1.0 失败了,但使用 gfortran 10.0.0 20190625),您可以考虑将参数设置为非延迟长度。毕竟,您到处都在使用 trim
,所以尾随空格会被压缩。
您的代码距离不合规仅一小步:写入语句不受可选虚拟对象存在性检查的保护。如果调用链中不存在可选的虚拟对象,则程序将无效。
我在子例程之间传递可选的延迟长度字符串 (character(len=:), allocatable, optional
),并在 GCC 中出现意外行为。
这是我的最小示例,其中我通过接口例程将可选字符串传递给设置它的例程:
$ cat main.f90
module deepest_call_m
implicit none
contains
subroutine deepest_call(str)
character(len=:), allocatable, intent(OUT), optional :: str
if (present(str)) str = '12345'
write(*,*) 'at bot of deepest_call, str is "'//trim(str)//'"'
end subroutine deepest_call
end module deepest_call_m
module interface_call_m
implicit none
contains
subroutine interface_call(str)
use deepest_call_m, only : deepest_call
character(len=:), allocatable, intent(OUT), optional :: str
call deepest_call(str=str)
write(*,*) 'at bot of interface_call, str is "'//trim(str)//'"'
end subroutine interface_call
end module interface_call_m
program main
use interface_call_m, only : interface_call
implicit none
character(len=:), allocatable :: str
call interface_call(str=str)
write(*,*) 'at bot of main, str is "'//trim(str)//'"'
end program main
(请注意,为简单起见,我没有将写入语句包装在 if(present)
和 if(allocated)
中,尽管这对于实际实现是必要的。)
在 Intel 16.0 和 2019_U4 以及 PGI 15.10 中,此例程给出了预期的结果:str
在 deepest_call
中设置并且在 interface_call
和在 main
:
$ ifort --version
ifort (IFORT) 16.0.0 20150815
Copyright (C) 1985-2015 Intel Corporation. All rights reserved.
$ ifort main.f90 && ./a.out
at bot of deepest_call, str is "12345"
at bot of interface_call, str is "12345"
at bot of main, str is "12345"
但是,对于 gfortran 4.8.5:
$ gfortran --version
GNU Fortran (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36)
Copyright (C) 2015 Free Software Foundation, Inc.
GNU Fortran comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of GNU Fortran
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING
$ gfortran main.f90 && ./a.out
at bot of deepest_call, str is "12345"
at bot of interface_call, str is ""
at bot of main, str is ""
从 interface_call
中的 deepest_call
返回时字符串已被截断。对于 gfortran 7.3.0 和 8.2.0,如果没有提供编译时选项,代码会在运行时崩溃:
[chaud106@epyc-login-1-0 Testing]$ gfortran --version && gfortran main.f90 && ./a.out
GNU Fortran (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
at bot of deepest_call, str is "12345"
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
Backtrace for this error:
#0 0x2af39a82733f in ???
#1 0x400c68 in ???
#2 0x400d9c in ???
#3 0x400f0e in ???
#4 0x2af39a813494 in ???
#5 0x400878 in ???
#6 0xffffffffffffffff in ???
Segmentation fault
但是,添加编译时检查可以恢复以前的截断行为:
$ gfortran --version
GNU Fortran (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gfortran -g -fbacktrace -Wall -Wextra -std=f2008 -fcheck=all -Og main.f90 && ./a.out
at bot of deepest_call, str is "12345"
at bot of interface_call, str is ""
at bot of main, str is ""
这对我来说很像编译器错误,所以也许我只需要 post 在 bugzilla 上。但我想先在这里得到一些反馈,特别是:
我的测试用例是否符合标准?我对
deepest_call
的str=str
参数特别好奇。当且仅当它在当前范围内是可选的时,我经常使用此结构将参数作为可选参数传递,但我无法在标准中轻松找到它,而且我不确定它是否真的有效。不过,仅传递str
似乎会产生相同的行为。是否有任何简单的解决方法?鉴于此问题影响范围广泛(4.8.5 到 8.2.0),简单地避免它们是不可行的。
是否有人知道其他版本的 gfortran 或其他编译器的这种行为?我只能轻松访问 GCC、Intel 和 PGI。
您的代码符合标准。
一个可选的伪参数在没有关键字的后续调用中可能是一个实际的伪参数,无论它是否存在。 (当然,只有在以下伪参数是可选的情况下才可能不存在。)即:
call interface_call(str)
与
一样正确call interface_call(str=str)
至于(次优)解决方法(我看到 gfortran 9.1.0 失败了,但使用 gfortran 10.0.0 20190625),您可以考虑将参数设置为非延迟长度。毕竟,您到处都在使用 trim
,所以尾随空格会被压缩。
您的代码距离不合规仅一小步:写入语句不受可选虚拟对象存在性检查的保护。如果调用链中不存在可选的虚拟对象,则程序将无效。