【发布时间】:2019-03-27 19:03:40
【问题描述】:
我想将旧版 Fortran 代码转换为现代 Fortran 兼容代码,这样我就可以打开编译器警告、接口检查等。在这个阶段我不想更改功能,只需让它像尽可能接近原来的样子,并且仍然让编译器满意。
我目前的问题是许多地方的代码传递了错误类型的数组,例如一个真正的数组到一个具有整数虚拟参数的子程序。这本身不是代码中的错误,因为它是故意的并且按预期工作(至少在常见配置中)。现在,我怎么能在保持代码兼容的同时做同样的事情呢?考虑以下示例:
program cast
implicit none
double precision :: a(10)
call fill_dble(a,10)
call print_dble(a,10)
call fill_int(a,10)
!call fill_int(cast_to_int(a),10)
call print_dble(a,10)
call print_int(a(1),10)
!call print_int(cast_to_int(a),10)
call print_dble(a(6),5)
contains
function cast_to_int(a) result(b)
use iso_c_binding
implicit none
double precision, target :: a(*)
integer, pointer :: b(:)
call c_f_pointer(c_loc(a(1)), b, [1])
end function
end program
subroutine fill_dble(b,n)
implicit none
integer :: n, i
double precision :: b(n)
do i = 1, n
b(i) = i
end do
end subroutine
subroutine print_dble(b,n)
implicit none
integer :: n
double precision :: b(n)
write(6,'(10es12.4)') b
end subroutine
subroutine fill_int(b,n)
implicit none
integer :: n, b(n), i
do i = 1, n
b(i) = i
end do
end subroutine
subroutine print_int(b,n)
implicit none
integer :: n, b(n)
write(6,'(10i4)') b
end subroutine
当我编译并运行它(gfortran 4.8 或 ifort 18)时,我得到了预期的结果:
1.0000E+00 2.0000E+00 3.0000E+00 4.0000E+00 5.0000E+00 6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
4.2440-314 8.4880-314 1.2732-313 1.6976-313 2.1220-313 6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
1 2 3 4 5 6 7 8 9 10
6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
实际数组的前半部分被整数破坏(因为整数是大小的一半),但是当打印为整数时,“正确”的值就在那里。但这是不合规的代码。当我尝试通过激活cast_to_int 函数(并在没有它的情况下禁用调用)来修复它时,我确实得到了一些在没有警告的情况下编译的东西,而使用 gfortran 我得到了相同的结果。然而,使用 ifort,我得到:
1.0000E+00 2.0000E+00 3.0000E+00 4.0000E+00 5.0000E+00 6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
1.0000E+00 2.0000E+00 3.0000E+00 4.0000E+00 5.0000E+00 6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
0******** 0 5 6 7 8 9 10
6.0000E+00 7.0000E+00 8.0000E+00 9.0000E+00 1.0000E+01
我无法理解。此外,带有-O0 的 ifort 会崩溃(而其他版本则不会)。
我知道代码仍然不太正确,因为cast_to_int 返回的指针大小仍然为 1,但我认为应该是另一个问题。
我做错了什么,或者我怎样才能让 ifort 做我想做的事?
编辑:在@VladimirF 的回复之后,我在implicit none 之后添加:
subroutine fill_int(b,n)
!dec$ attributes no_arg_check :: b
integer :: n, b(n)
end subroutine
subroutine print_int(b,n)
!dec$ attributes no_arg_check :: b
integer :: n, b(n)
end subroutine
end interface
但是编译时出现警告仍然给我一个错误:
$ ifort cast2.f90 -warn all
cast2.f90(17): error #6633: The type of the actual argument differs from the type of the dummy argument. [A]
call fill_int(a,10)
--------------^
cast2.f90(20): error #6633: The type of the actual argument differs from the type of the dummy argument. [A]
call print_int(a(1),10)
---------------^
compilation aborted for cast2.f90 (code 1)
【问题讨论】:
-
您可以使用
transfer,但您必须以某种方式更改例程的逻辑。我知道没有可移植的方法来通过参数实现这一点。但是,您应该首先尝试了解原作者为什么首先这样做,以及如何正确地做同样的事情(可能是 X/Y 问题),可能没有这样的 hack。 -
“保持代码合规”是什么意思?代码肯定不符合 Fortran 标准。
-
@Steve 我的意思是编译器告诉我的。如果没有
cast_to_int,它显然不符合要求。我想要一些行为相同(不仅仅是给出相同结果)并且符合要求的东西,或者至少给出尽可能少的警告。 -
@Jellby 出于好奇,如果您将“双精度,目标 :: a(*)”更改为“双精度,目标 :: a(:)”,ifort 是否给出与gfortran 取消注释 cast_to_int() + 注释掉调用时没有它?
-
@roygvib 不,它与
a(*)或a(:)给出相同的结果
标签: pointers fortran legacy-code