Pointers Fortran中的有意类型不匹配

Pointers Fortran中的有意类型不匹配,pointers,fortran,legacy-code,Pointers,Fortran,Legacy Code,我想把传统的Fortran代码转换成现代的Fortran兼容代码,这样我就可以打开编译器警告、接口检查等功能。在这个阶段,我不想改变功能,只想让它尽可能接近原来的功能,并且仍然让编译器满意 我当前的问题是,代码在许多地方传递了错误类型的数组,例如,一个实数组传递给一个具有整数伪参数的子例程。这本身不是代码中的一个bug,因为它是故意的,并且按照预期工作(至少在常见配置中)。现在,我如何在保持代码兼容的同时做到同样的事情呢?考虑下面的例子: program cast implicit none d

我想把传统的Fortran代码转换成现代的Fortran兼容代码,这样我就可以打开编译器警告、接口检查等功能。在这个阶段,我不想改变功能,只想让它尽可能接近原来的功能,并且仍然让编译器满意

我当前的问题是,代码在许多地方传递了错误类型的数组,例如,一个实数组传递给一个具有整数伪参数的子例程。这本身不是代码中的一个bug,因为它是故意的,并且按照预期工作(至少在常见配置中)。现在,我如何在保持代码兼容的同时做到同样的事情呢?考虑下面的例子:

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\u to\u int
返回的指针大小仍然是1,但我认为这应该是另一个问题

我做错了什么,或者我怎样才能得到我想要的呢


编辑:在@VladimirF的回复之后,我在
隐式无
之后添加:

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)

英特尔Fortran支持。它指示编译器“忽略与显式接口相关的类型和形状匹配规则”

它可以应用于单个伪参数名称或例程名称,在这种情况下,该选项将应用于该接口中的所有伪参数

它应该应用于模块过程(或接口块),因此您应该将函数和子例程移动到模块中

许多其他编译器都有


你的代码有什么问题?根据经验,永远不要使用任何返回
指针的Fortran函数。他们是纯粹的邪恶。Fortran指针与C指针完全不同

当您执行
调用fill_int(cast_to_int(a),10)
时,将计算表达式
cast_to_int(a)
,结果是一个数组。现在,根据优化,编译器可以选择传递原始指针的地址,但也可以创建结果整数数组的副本,并将副本传递给子例程

另外,数组
a
没有
target
属性,因此
cast\u to\u int(a)
中使用的地址仅在函数内部有效,返回后无效


您应该在主程序中设置
b
,只需通过
b
,而不是
a
。它的工作原理与等价性类似。查看存储为不同类型的值无论如何都不符合标准。这种类型的双关语是不允许的。

我找到了一种可能的通用解决方案,似乎很有效。我必须处理的代码如下所示:

subroutine some_subroutine(a,b,c,d,...)
real a(*),b(*),c(*),d(*)
! many more declarations, including common blocks

!...
call other_subroutine(a,b(idx),c,...)
!...

end subroutine some_subroutine

! this typically in another file:
subroutine other_subroutine(x,y,z,...)
real x(*)
integer y(*)
logical z(*)
! other declarations and common blocks

! unreadable code with calls to other procedures
! not clear which which arguments are input and output

end subroutine other_subroutine
我现在将其修改为:

subroutine some_subroutine(a,b,c,d,...)
real a(*),b(*),c(*),d(*)
! many more declarations, including common blocks

call inner_sub(b,c)

contains
subroutine inner_sub(b,c)
use iso_c_binding
real, target :: b(*),c(*)
integer, pointer :: ib(:)
logical, pointer :: lc(:)

!...
call c_f_pointer(c_loc(b(idx)),ib,[1]) ! or use the actual length if I can figure it out
call c_f_pointer(c_loc(c(1)),lc,[1])
call other_subroutine(a,ib,lc,...)
nullify(ib,lc)
!...

end subroutine inner_sub

end subroutine some_subroutine
保持
其他子例程
不变。如果我直接在外部例程上使用
target
属性,我必须为调用它的任何东西添加一个显式接口,因此我包装内部代码。通过使用
contains
我不需要传递所有变量,只需要传递那些将被“双关”的变量。
c\u f\u指针
调用应该在有问题的调用之前完成,因为索引变量(
idx
在示例中)可能位于公共块中,并在其他调用中更改,例如


除了原始代码中已经存在的缺陷之外,还有什么缺陷吗?

我试图避免它,因为首先,它需要编写接口块或模块,其次,不检查与显式执行“转换”不同。@Jellby这真的不是一个很大的限制,我想你真的想要太多了。您希望发生什么样的转换?自动的?您不能仅仅将整数位模式重新解释为实数并期望得到有意义的数字。也许您还应该显示原始方法的代码,以便我们可以看到您实际尝试的操作。当然,我对数字的含义没有任何期望,但原始作者认为它会起作用,显然,几十年来一直如此。我手头没有一个具体的例子,但我在代码中的几个地方看到过它(大而凌乱的代码库),通常就像英特尔网页上说的那样,它是内存复制例程,或者读/写二进制文件,或者分配一个大的块作为实块,然后使用其中的一些块作为整数。仅将数组的不同部分
H
用于参数,而子例程在中定义为实数和整数参数。您可以使用
transfer
,但必须以某种方式更改例程的逻辑。我知道没有可移植的方法来实现这一点。然而,你应该首先试着理解为什么最初的作者确实这样做了,以及如何正确地做同样的事情(可能是一个X/Y问题),可能没有这样的黑客。你说的“保持代码兼容”是什么意思?这段代码当然不符合Fortran标准。@Steve我的意思是编译器告诉我的。如果没有
cast\u to\u int
,则显然不符合要求。我想要的是行为相同(不仅仅是给出相同的结果)且符合要求的东西,或者至少是发出尽可能少的警告的东西。@Jellby出于好奇,如果您将“双精度,目标::a(*)更改为“双精度”