Fortran 的 "final" 子程序是否足够可靠以供实际使用?
Are Fortran's "final" subroutines reliable enough for practical use?
现代 Fortran 包含各种面向对象的思想,包括 "destructors" 通过 FINAL
关键字的概念。
MODULE mobject
TYPE :: tobject
! Data declarations
CONTAINS
FINAL :: finalize
END TYPE
CONTAINS
SUBROUTINE finalize(object)
TYPE(tobject) object
...
END SUBROUTINE
END MODULE
但是,这个功能靠谱吗?值得注意的是,我注意到关于何时以及是否会调用它的不一致,英特尔 Fortran 19 和 GFortan 7、8 之间存在重大差异:
- GFortran 无法销毁存储在数组中的对象。
- 英特尔 Fortran:
- 在分配时执行虚假的和潜在的多余破坏,甚至可能在包含垃圾数据的内存上,并且
- 在从函数 returning 时对析构函数执行虚假调用。
我注意到 gfortran-7.4.0 和 gfortran-8.2.1.2 之间没有区别。
这些不一致对我提出了一些关于析构函数实际可用性的问题。这两种行为是否完全符合标准?这个标准不清楚吗?该标准是否可能包含导致非直觉行为的条款?
详细分析(代码见下)
PROGRAM 块。 Gfortran 不会为在 PROGRAM 块中声明的实例调用析构函数,而 Ifort 会(参见示例中的 run1
) .
标量对象。对于声明为标量的实例,如果变量已经过任何形式的初始化,Gfortran 和 IFort 都会调用析构函数。然而,Intel Fortran 在分配函数 return 值时,也会调用它
- 在堆栈上未初始化的对象上,然后用函数中的数据覆盖它,并且
- 貌似在
newObject
函数的末尾。
然而,这可以通过显式检查是否
在执行任何清理之前初始化对象。
这意味着,程序员必须显式检查实例是否已初始化。
数组中的对象。如果对象包含在数组中,并且数组超出范围,
- Gfortran 不会调用析构函数。
- 英特尔 Fortran 可能会调用析构函数,具体取决于给定数组成员的初始化方式。
- 数组是否声明
allocatable
. 没有区别
Allocatable array initialized by assignment.当使用现代功能时,对可分配数组的分配意味着分配,除了没有IntelFortran 可以调用析构函数的未初始化实例。
Allocatable/Pointers 来自函数。
- GFortran 不会在函数末尾调用析构函数 returning 一个
allocatable
对象或一个 pointer
对象,而是在值在客户端代码中被释放,明确地或通过超出 allocatable
s 的范围。这就是我所期望的。
- 英特尔 Fortran 在其他一些情况下调用:
- 当对象被声明时
allocatable
,而不是当它是 pointer
时,英特尔 Fortran 在退出函数时调用函数局部值的析构函数。
- 当使用隐式分配 (
var = newObject(...)
) 或在 pointer
变体的情况下,使用显式分配 (allocate(var); var = newObject(...)
) 初始化函数内的对象时,析构函数是在未初始化的内存上调用,在 run5MoveAlloc
和 run6MovePtr
中可见,来自 %name
包含垃圾数据。这可以通过使用 allocate(var); call var%init(...)
模式来解决。
测试代码
!! -- Makefile ---------------------------------------------------
!! Runs the code with various compilers.
SHELL = bash
FC = NO_COMPILER_SPECIFIED
COMPILERS = gfortran-7 gfortran-8 ifort
PR = @echo$(n)pr -m -t -w 100
define n
endef
all:
rm -rf *.mod *.bin
$(foreach FC, $(COMPILERS), $(n)\
rm -rf *.mod && \
$(FC) destructor.f90 -o $(FC).bin && \
chmod +x $(FC).bin)
$(PR) $(foreach FC, $(COMPILERS), <(head -1 <($(FC) --version)))
$(info)
$(foreach N,0 1 2 3 4 5 6,$(n) \
$(PR) $(foreach FC, $(COMPILERS), <(./$(FC).bin $(N))))
!! -- destructor.f90 ---------------------------------------------
module mobject
implicit none
private
public tobject, newObject
type :: tobject
character(32) :: name = "<undef>"
contains
procedure :: init
final :: finalize
end type tobject
contains
subroutine init(object, name)
class(tobject), intent(inout) :: object
character(*), intent(in) :: name
print *, "+ ", name
object%name = name
end subroutine init
function newObject(name)
type(tobject) :: newObject
character(*), intent(in) :: name
call new%init(name)
end function newObject
subroutine finalize(object)
type(tobject) :: object
print *, "- ", object%name
end subroutine finalize
end module mobject
module mrun
use mobject
implicit none
contains
subroutine run1()
type(tobject) :: o1_uninit, o2_field_assigned, o3_tobject, o4_new, o6_init
type(tobject), allocatable :: o5_new_alloc, o7_init_alloc
print *, ">>>>> run1"
o2_field_assigned%name = "o2_field_assigned"
o3_tobject = tobject("o3_tobject")
o4_new = newObject("o4_new")
o5_new_alloc = newObject("o5_new_alloc")
call o6_init%init("o6_init")
allocate(o7_init_alloc)
call o7_init_alloc%init("o7_init_alloc")
print *, "<<<<< run1"
end subroutine run1
subroutine run2Array()
type(tobject) :: objects(4)
print *, ">>>>> run2Array"
objects(1)%name = "objects(1)_uninit"
objects(2) = tobject("objects(2)_tobject")
objects(3) = newObject("objects(3)_new")
call objects(4)%init("objects(4)_init")
print *, "<<<<< run2Array"
end subroutine run2Array
subroutine run3AllocArr()
type(tobject), allocatable :: objects(:)
print *, ">>>>> run3AllocArr"
allocate(objects(4))
objects(1)%name = "objects(1)_uninit"
objects(2) = tobject("objects(2)_tobject")
objects(3) = newObject("objects(3)_new")
call objects(4)%init("objects(4)_init")
print *, "<<<<< run3AllocArr"
end subroutine run3AllocArr
subroutine run4AllocArrAssgn()
type(tobject), allocatable :: objects(:)
print *, ">>>>> run4AllocArrAssgn"
objects = [ &
tobject("objects(1)_tobject"), &
newObject("objects(2)_new") ]
print *, "<<<<< run4AllocArrAssgn"
end subroutine run4AllocArrAssgn
subroutine run5MoveAlloc()
type(tobject), allocatable :: o_alloc
print *, ">>>>> run5MoveAlloc"
o_alloc = getAlloc()
print *, "<<<<< run5MoveAlloc"
end subroutine run5MoveAlloc
function getAlloc() result(object)
type(tobject), allocatable :: object
print *, ">>>>> getAlloc"
allocate(object)
object = newObject("o_alloc")
print *, "<<<<< getAlloc"
end function getAlloc
subroutine run6MovePtr()
type(tobject), pointer :: o_pointer
print *, ">>>>> run6MovePtr"
o_pointer => getPtr()
deallocate(o_pointer)
print *, "<<<<< run6MovePtr"
end subroutine run6MovePtr
function getPtr() result(object)
type(tobject), pointer :: object
print *, ">>>>> getPtr"
allocate(object)
object = newObject("o_pointer")
print *, "<<<<< getPtr"
end function getPtr
end module mrun
program main
use mobject
use mrun
implicit none
type(tobject) :: object
character(1) :: argument
print *, ">>>>> main"
call get_command_argument(1, argument)
select case (argument)
case("1")
call run1()
case("2")
call run2Array()
case("3")
call run3AllocArr()
case("4")
call run4AllocArrAssgn()
case("5")
call run5MoveAlloc()
case("6")
call run6MovePtr()
case("0")
print *, "####################";
print *, ">>>>> runDirectlyInMain"
object = newObject("object_in_main")
print *, "<<<<< runDirectlyInMain"
case default
print *, "Incorrect commandline argument"
end select
print *, "<<<<< main"
end program main
测试代码的输出
>> make
rm -rf *.mod *.bin
rm -rf *.mod && gfortran-7 destructor.f90 -o gfortran-7.bin && chmod +x gfortran-7.bin
rm -rf *.mod && gfortran-8 destructor.f90 -o gfortran-8.bin && chmod +x gfortran-8.bin
rm -rf *.mod && ifort destructor.f90 -o ifort.bin && chmod +x ifort.bin
pr -m -t -w 100 <(head -1 <(gfortran-7 --version)) <(head -1 <(gfortran-8 --version)) <(head -1 <(ifort --version))
GNU Fortran (SUSE Linux) 7.4.0 GNU Fortran (SUSE Linux) 8.2.1 2 ifort (IFORT) 19.0.4.243 2019041
pr -m -t -w 100 <(./gfortran-7.bin 0) <(./gfortran-8.bin 0) <(./ifort.bin 0)
>>>>> main >>>>> main >>>>> main
#################### #################### ####################
>>>>> runDirectlyInMain >>>>> runDirectlyInMain >>>>> runDirectlyInMain
+ object_in_main + object_in_main + object_in_main
<<<<< runDirectlyInMain <<<<< runDirectlyInMain - <undef>
<<<<< main <<<<< main - object_in_main
<<<<< runDirectlyInMain
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 1) <(./gfortran-8.bin 1) <(./ifort.bin 1)
>>>>> main >>>>> main >>>>> main
>>>>> run1 >>>>> run1 >>>>> run1
+ o4_new + o4_new - <undef>
+ o5_new_alloc + o5_new_alloc + o4_new
+ o6_init + o6_init - <undef>
+ o7_init_alloc + o7_init_alloc - o4_new
<<<<< run1 <<<<< run1 + o5_new_alloc
- o7_init_alloc - o7_init_alloc - o5_new_alloc
- o6_init - o6_init + o6_init
- o5_new_alloc - o5_new_alloc + o7_init_alloc
- o4_new - o4_new <<<<< run1
- o3_tobject - o3_tobject - <undef>
- o2_field_assigned - o2_field_assigned - o2_field_assigned
<<<<< main <<<<< main - o3_tobject
- o4_new
- o6_init
- o5_new_alloc
- o7_init_alloc
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 2) <(./gfortran-8.bin 2) <(./ifort.bin 2)
>>>>> main >>>>> main >>>>> main
>>>>> run2Array >>>>> run2Array >>>>> run2Array
+ objects(3)_new + objects(3)_new - <undef>
+ objects(4)_init + objects(4)_init + objects(3)_new
<<<<< run2Array <<<<< run2Array - <undef>
<<<<< main <<<<< main - objects(3)_new
+ objects(4)_init
<<<<< run2Array
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 3) <(./gfortran-8.bin 3) <(./ifort.bin 3)
>>>>> main >>>>> main >>>>> main
>>>>> run3AllocArr >>>>> run3AllocArr >>>>> run3AllocArr
+ objects(3)_new + objects(3)_new - <undef>
+ objects(4)_init + objects(4)_init + objects(3)_new
<<<<< run3AllocArr <<<<< run3AllocArr - <undef>
<<<<< main <<<<< main - objects(3)_new
+ objects(4)_init
<<<<< run3AllocArr
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 4) <(./gfortran-8.bin 4) <(./ifort.bin 4)
>>>>> main >>>>> main >>>>> main
>>>>> run4AllocArrAssgn >>>>> run4AllocArrAssgn >>>>> run4AllocArrAssgn
+ objects(2)_new + objects(2)_new + objects(2)_new
<<<<< run4AllocArrAssgn <<<<< run4AllocArrAssgn - objects(2)_new
<<<<< main <<<<< main <<<<< run4AllocArrAssgn
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 5) <(./gfortran-8.bin 5) <(./ifort.bin 5)
>>>>> main >>>>> main >>>>> main
>>>>> run5MoveAlloc >>>>> run5MoveAlloc >>>>> run5MoveAlloc
>>>>> getAlloc >>>>> getAlloc >>>>> getAlloc
+ o_alloc + o_alloc + o_alloc
<<<<< getAlloc <<<<< getAlloc - `4�[=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=]
<<<<< run5MoveAlloc <<<<< run5MoveAlloc - o_alloc
- o_alloc - o_alloc <<<<< getAlloc
<<<<< main <<<<< main - o_alloc
<<<<< run5MoveAlloc
- o_alloc
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 6) <(./gfortran-8.bin 6) <(./ifort.bin 6)
>>>>> main >>>>> main >>>>> main
>>>>> run6MovePtr >>>>> run6MovePtr >>>>> run6MovePtr
>>>>> getPtr >>>>> getPtr >>>>> getPtr
+ o_pointer + o_pointer + o_pointer
<<<<< getPtr <<<<< getPtr - `��[=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=]
- o_pointer - o_pointer - o_pointer
<<<<< run6MovePtr <<<<< run6MovePtr <<<<< getPtr
<<<<< main <<<<< main - o_pointer
<<<<< run6MovePtr
<<<<< main
TLDR:Gfortran 中存在已知的未解决问题。英特尔声称全力支持。一些编译器声称不支持。
一般来说,关于可靠性和可用性的问题是相当主观的,因为你必须考虑许多你独有的点(你需要支持多个编译器吗?你需要支持他们的旧版本吗?究竟是哪些? ? 如果某个实体没有最终确定,这有多重要?)。
你提出了一些没有实际代码示例很难回答的主张,并且可能是一个单独的完整问题和答案的主题。 Gfortran 在此错误报告 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37336 (the link points to a meta-bug that points to several individual issues tracked in the bugzilla). It is known and that the feature is not finished and that there are outstanding issues. Most notably (at least for me), function results are not being finalized. An overview of the status of other compilers (simplified to Y/N/paritally) is at http://fortranwiki.org/fortran/show/Fortran+2003+status 中发布了 Fortran 2003 和 2008 功能的当前实施状态,并在 Fortran 论坛文章中定期更新。
我不能谈论那些所谓的 Intel Fortran 的虚假定稿。如果您在编译器中发现错误,您应该向供应商提交错误报告。英特尔通常反应迅速。
不过,有些个别问题可以得到解答。您可能会找到关于它们的单独 Q/As。但是:
Gfortran 不会为 PROGRAM 块中声明的实例调用析构函数,而 Ifort 会(参见示例中的 run1)。
- 在主程序中声明的变量按照标准隐式获取
save
属性。编译器不应生成任何自动终结。
Intel Fortran 然而,当分配一个函数 return 值时,也会调用它
- 正如 Gfortran bugzilla 中指出的那样,gfortran 尚未最终确定函数结果变量。
这意味着,程序员必须显式检查实例是否已初始化。
- Fortran 标准中恐怕没有这样的概念。我不知道“如果变量已经看到任何形式的初始化”是什么意思。请注意,初始化函数 与任何其他函数一样 。
当使用现代功能时,对可分配数组的赋值意味着分配,这同样适用,除了没有未初始化的实例可供 IntelFortran 调用析构函数。
- 不确定这到底是什么意思。 Fortran 中没有这样的"initialization"。也许功能结果又来了?
Allocatable/Pointers 来自函数。
- 正如多次指出的那样,函数结果在当前版本的 Gfortran 中没有正确完成。
如果你想详细回答任何一点,你真的必须提出一个具体的问题。这个太宽泛了。此站点的 help/instructions 包含 "Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the [ask] for help clarifying this question."
现代 Fortran 包含各种面向对象的思想,包括 "destructors" 通过 FINAL
关键字的概念。
MODULE mobject
TYPE :: tobject
! Data declarations
CONTAINS
FINAL :: finalize
END TYPE
CONTAINS
SUBROUTINE finalize(object)
TYPE(tobject) object
...
END SUBROUTINE
END MODULE
但是,这个功能靠谱吗?值得注意的是,我注意到关于何时以及是否会调用它的不一致,英特尔 Fortran 19 和 GFortan 7、8 之间存在重大差异:
- GFortran 无法销毁存储在数组中的对象。
- 英特尔 Fortran:
- 在分配时执行虚假的和潜在的多余破坏,甚至可能在包含垃圾数据的内存上,并且
- 在从函数 returning 时对析构函数执行虚假调用。
我注意到 gfortran-7.4.0 和 gfortran-8.2.1.2 之间没有区别。
这些不一致对我提出了一些关于析构函数实际可用性的问题。这两种行为是否完全符合标准?这个标准不清楚吗?该标准是否可能包含导致非直觉行为的条款?
详细分析(代码见下)
PROGRAM 块。 Gfortran 不会为在 PROGRAM 块中声明的实例调用析构函数,而 Ifort 会(参见示例中的
run1
) .标量对象。对于声明为标量的实例,如果变量已经过任何形式的初始化,Gfortran 和 IFort 都会调用析构函数。然而,Intel Fortran 在分配函数 return 值时,也会调用它
- 在堆栈上未初始化的对象上,然后用函数中的数据覆盖它,并且
- 貌似在
newObject
函数的末尾。
然而,这可以通过显式检查是否 在执行任何清理之前初始化对象。
这意味着,程序员必须显式检查实例是否已初始化。
数组中的对象。如果对象包含在数组中,并且数组超出范围,
- Gfortran 不会调用析构函数。
- 英特尔 Fortran 可能会调用析构函数,具体取决于给定数组成员的初始化方式。
- 数组是否声明
allocatable
. 没有区别
Allocatable array initialized by assignment.当使用现代功能时,对可分配数组的分配意味着分配,除了没有IntelFortran 可以调用析构函数的未初始化实例。
Allocatable/Pointers 来自函数。
- GFortran 不会在函数末尾调用析构函数 returning 一个
allocatable
对象或一个pointer
对象,而是在值在客户端代码中被释放,明确地或通过超出allocatable
s 的范围。这就是我所期望的。 - 英特尔 Fortran 在其他一些情况下调用:
- 当对象被声明时
allocatable
,而不是当它是pointer
时,英特尔 Fortran 在退出函数时调用函数局部值的析构函数。 - 当使用隐式分配 (
var = newObject(...)
) 或在pointer
变体的情况下,使用显式分配 (allocate(var); var = newObject(...)
) 初始化函数内的对象时,析构函数是在未初始化的内存上调用,在run5MoveAlloc
和run6MovePtr
中可见,来自%name
包含垃圾数据。这可以通过使用allocate(var); call var%init(...)
模式来解决。
- 当对象被声明时
- GFortran 不会在函数末尾调用析构函数 returning 一个
测试代码
!! -- Makefile ---------------------------------------------------
!! Runs the code with various compilers.
SHELL = bash
FC = NO_COMPILER_SPECIFIED
COMPILERS = gfortran-7 gfortran-8 ifort
PR = @echo$(n)pr -m -t -w 100
define n
endef
all:
rm -rf *.mod *.bin
$(foreach FC, $(COMPILERS), $(n)\
rm -rf *.mod && \
$(FC) destructor.f90 -o $(FC).bin && \
chmod +x $(FC).bin)
$(PR) $(foreach FC, $(COMPILERS), <(head -1 <($(FC) --version)))
$(info)
$(foreach N,0 1 2 3 4 5 6,$(n) \
$(PR) $(foreach FC, $(COMPILERS), <(./$(FC).bin $(N))))
!! -- destructor.f90 ---------------------------------------------
module mobject
implicit none
private
public tobject, newObject
type :: tobject
character(32) :: name = "<undef>"
contains
procedure :: init
final :: finalize
end type tobject
contains
subroutine init(object, name)
class(tobject), intent(inout) :: object
character(*), intent(in) :: name
print *, "+ ", name
object%name = name
end subroutine init
function newObject(name)
type(tobject) :: newObject
character(*), intent(in) :: name
call new%init(name)
end function newObject
subroutine finalize(object)
type(tobject) :: object
print *, "- ", object%name
end subroutine finalize
end module mobject
module mrun
use mobject
implicit none
contains
subroutine run1()
type(tobject) :: o1_uninit, o2_field_assigned, o3_tobject, o4_new, o6_init
type(tobject), allocatable :: o5_new_alloc, o7_init_alloc
print *, ">>>>> run1"
o2_field_assigned%name = "o2_field_assigned"
o3_tobject = tobject("o3_tobject")
o4_new = newObject("o4_new")
o5_new_alloc = newObject("o5_new_alloc")
call o6_init%init("o6_init")
allocate(o7_init_alloc)
call o7_init_alloc%init("o7_init_alloc")
print *, "<<<<< run1"
end subroutine run1
subroutine run2Array()
type(tobject) :: objects(4)
print *, ">>>>> run2Array"
objects(1)%name = "objects(1)_uninit"
objects(2) = tobject("objects(2)_tobject")
objects(3) = newObject("objects(3)_new")
call objects(4)%init("objects(4)_init")
print *, "<<<<< run2Array"
end subroutine run2Array
subroutine run3AllocArr()
type(tobject), allocatable :: objects(:)
print *, ">>>>> run3AllocArr"
allocate(objects(4))
objects(1)%name = "objects(1)_uninit"
objects(2) = tobject("objects(2)_tobject")
objects(3) = newObject("objects(3)_new")
call objects(4)%init("objects(4)_init")
print *, "<<<<< run3AllocArr"
end subroutine run3AllocArr
subroutine run4AllocArrAssgn()
type(tobject), allocatable :: objects(:)
print *, ">>>>> run4AllocArrAssgn"
objects = [ &
tobject("objects(1)_tobject"), &
newObject("objects(2)_new") ]
print *, "<<<<< run4AllocArrAssgn"
end subroutine run4AllocArrAssgn
subroutine run5MoveAlloc()
type(tobject), allocatable :: o_alloc
print *, ">>>>> run5MoveAlloc"
o_alloc = getAlloc()
print *, "<<<<< run5MoveAlloc"
end subroutine run5MoveAlloc
function getAlloc() result(object)
type(tobject), allocatable :: object
print *, ">>>>> getAlloc"
allocate(object)
object = newObject("o_alloc")
print *, "<<<<< getAlloc"
end function getAlloc
subroutine run6MovePtr()
type(tobject), pointer :: o_pointer
print *, ">>>>> run6MovePtr"
o_pointer => getPtr()
deallocate(o_pointer)
print *, "<<<<< run6MovePtr"
end subroutine run6MovePtr
function getPtr() result(object)
type(tobject), pointer :: object
print *, ">>>>> getPtr"
allocate(object)
object = newObject("o_pointer")
print *, "<<<<< getPtr"
end function getPtr
end module mrun
program main
use mobject
use mrun
implicit none
type(tobject) :: object
character(1) :: argument
print *, ">>>>> main"
call get_command_argument(1, argument)
select case (argument)
case("1")
call run1()
case("2")
call run2Array()
case("3")
call run3AllocArr()
case("4")
call run4AllocArrAssgn()
case("5")
call run5MoveAlloc()
case("6")
call run6MovePtr()
case("0")
print *, "####################";
print *, ">>>>> runDirectlyInMain"
object = newObject("object_in_main")
print *, "<<<<< runDirectlyInMain"
case default
print *, "Incorrect commandline argument"
end select
print *, "<<<<< main"
end program main
测试代码的输出
>> make
rm -rf *.mod *.bin
rm -rf *.mod && gfortran-7 destructor.f90 -o gfortran-7.bin && chmod +x gfortran-7.bin
rm -rf *.mod && gfortran-8 destructor.f90 -o gfortran-8.bin && chmod +x gfortran-8.bin
rm -rf *.mod && ifort destructor.f90 -o ifort.bin && chmod +x ifort.bin
pr -m -t -w 100 <(head -1 <(gfortran-7 --version)) <(head -1 <(gfortran-8 --version)) <(head -1 <(ifort --version))
GNU Fortran (SUSE Linux) 7.4.0 GNU Fortran (SUSE Linux) 8.2.1 2 ifort (IFORT) 19.0.4.243 2019041
pr -m -t -w 100 <(./gfortran-7.bin 0) <(./gfortran-8.bin 0) <(./ifort.bin 0)
>>>>> main >>>>> main >>>>> main
#################### #################### ####################
>>>>> runDirectlyInMain >>>>> runDirectlyInMain >>>>> runDirectlyInMain
+ object_in_main + object_in_main + object_in_main
<<<<< runDirectlyInMain <<<<< runDirectlyInMain - <undef>
<<<<< main <<<<< main - object_in_main
<<<<< runDirectlyInMain
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 1) <(./gfortran-8.bin 1) <(./ifort.bin 1)
>>>>> main >>>>> main >>>>> main
>>>>> run1 >>>>> run1 >>>>> run1
+ o4_new + o4_new - <undef>
+ o5_new_alloc + o5_new_alloc + o4_new
+ o6_init + o6_init - <undef>
+ o7_init_alloc + o7_init_alloc - o4_new
<<<<< run1 <<<<< run1 + o5_new_alloc
- o7_init_alloc - o7_init_alloc - o5_new_alloc
- o6_init - o6_init + o6_init
- o5_new_alloc - o5_new_alloc + o7_init_alloc
- o4_new - o4_new <<<<< run1
- o3_tobject - o3_tobject - <undef>
- o2_field_assigned - o2_field_assigned - o2_field_assigned
<<<<< main <<<<< main - o3_tobject
- o4_new
- o6_init
- o5_new_alloc
- o7_init_alloc
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 2) <(./gfortran-8.bin 2) <(./ifort.bin 2)
>>>>> main >>>>> main >>>>> main
>>>>> run2Array >>>>> run2Array >>>>> run2Array
+ objects(3)_new + objects(3)_new - <undef>
+ objects(4)_init + objects(4)_init + objects(3)_new
<<<<< run2Array <<<<< run2Array - <undef>
<<<<< main <<<<< main - objects(3)_new
+ objects(4)_init
<<<<< run2Array
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 3) <(./gfortran-8.bin 3) <(./ifort.bin 3)
>>>>> main >>>>> main >>>>> main
>>>>> run3AllocArr >>>>> run3AllocArr >>>>> run3AllocArr
+ objects(3)_new + objects(3)_new - <undef>
+ objects(4)_init + objects(4)_init + objects(3)_new
<<<<< run3AllocArr <<<<< run3AllocArr - <undef>
<<<<< main <<<<< main - objects(3)_new
+ objects(4)_init
<<<<< run3AllocArr
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 4) <(./gfortran-8.bin 4) <(./ifort.bin 4)
>>>>> main >>>>> main >>>>> main
>>>>> run4AllocArrAssgn >>>>> run4AllocArrAssgn >>>>> run4AllocArrAssgn
+ objects(2)_new + objects(2)_new + objects(2)_new
<<<<< run4AllocArrAssgn <<<<< run4AllocArrAssgn - objects(2)_new
<<<<< main <<<<< main <<<<< run4AllocArrAssgn
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 5) <(./gfortran-8.bin 5) <(./ifort.bin 5)
>>>>> main >>>>> main >>>>> main
>>>>> run5MoveAlloc >>>>> run5MoveAlloc >>>>> run5MoveAlloc
>>>>> getAlloc >>>>> getAlloc >>>>> getAlloc
+ o_alloc + o_alloc + o_alloc
<<<<< getAlloc <<<<< getAlloc - `4�[=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=]
<<<<< run5MoveAlloc <<<<< run5MoveAlloc - o_alloc
- o_alloc - o_alloc <<<<< getAlloc
<<<<< main <<<<< main - o_alloc
<<<<< run5MoveAlloc
- o_alloc
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 6) <(./gfortran-8.bin 6) <(./ifort.bin 6)
>>>>> main >>>>> main >>>>> main
>>>>> run6MovePtr >>>>> run6MovePtr >>>>> run6MovePtr
>>>>> getPtr >>>>> getPtr >>>>> getPtr
+ o_pointer + o_pointer + o_pointer
<<<<< getPtr <<<<< getPtr - `��[=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=]
- o_pointer - o_pointer - o_pointer
<<<<< run6MovePtr <<<<< run6MovePtr <<<<< getPtr
<<<<< main <<<<< main - o_pointer
<<<<< run6MovePtr
<<<<< main
TLDR:Gfortran 中存在已知的未解决问题。英特尔声称全力支持。一些编译器声称不支持。
一般来说,关于可靠性和可用性的问题是相当主观的,因为你必须考虑许多你独有的点(你需要支持多个编译器吗?你需要支持他们的旧版本吗?究竟是哪些? ? 如果某个实体没有最终确定,这有多重要?)。
你提出了一些没有实际代码示例很难回答的主张,并且可能是一个单独的完整问题和答案的主题。 Gfortran 在此错误报告 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37336 (the link points to a meta-bug that points to several individual issues tracked in the bugzilla). It is known and that the feature is not finished and that there are outstanding issues. Most notably (at least for me), function results are not being finalized. An overview of the status of other compilers (simplified to Y/N/paritally) is at http://fortranwiki.org/fortran/show/Fortran+2003+status 中发布了 Fortran 2003 和 2008 功能的当前实施状态,并在 Fortran 论坛文章中定期更新。
我不能谈论那些所谓的 Intel Fortran 的虚假定稿。如果您在编译器中发现错误,您应该向供应商提交错误报告。英特尔通常反应迅速。
不过,有些个别问题可以得到解答。您可能会找到关于它们的单独 Q/As。但是:
Gfortran 不会为 PROGRAM 块中声明的实例调用析构函数,而 Ifort 会(参见示例中的 run1)。
- 在主程序中声明的变量按照标准隐式获取
save
属性。编译器不应生成任何自动终结。
- 在主程序中声明的变量按照标准隐式获取
Intel Fortran 然而,当分配一个函数 return 值时,也会调用它
- 正如 Gfortran bugzilla 中指出的那样,gfortran 尚未最终确定函数结果变量。
这意味着,程序员必须显式检查实例是否已初始化。
- Fortran 标准中恐怕没有这样的概念。我不知道“如果变量已经看到任何形式的初始化”是什么意思。请注意,初始化函数 与任何其他函数一样 。
当使用现代功能时,对可分配数组的赋值意味着分配,这同样适用,除了没有未初始化的实例可供 IntelFortran 调用析构函数。
- 不确定这到底是什么意思。 Fortran 中没有这样的"initialization"。也许功能结果又来了?
Allocatable/Pointers 来自函数。
- 正如多次指出的那样,函数结果在当前版本的 Gfortran 中没有正确完成。
如果你想详细回答任何一点,你真的必须提出一个具体的问题。这个太宽泛了。此站点的 help/instructions 包含 "Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the [ask] for help clarifying this question."