簡體   English   中英

Fortran中的故意類型不匹配

[英]Intentional type mismatch in Fortran

我想將傳統的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

我無法理解。 此外,ifort與-O0崩潰(並且與其他版本不同)。

我知道代碼仍然不太正確,因為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)

我找到了一個似乎有效的通用解決方案。 我必須處理的代碼看起來像這樣:

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

other_subroutine保持不變。 如果我直接在外部例程上使用target屬性,我必須為調用它的任何東西添加一個顯式接口,所以我將包裝內部代碼。 通過使用contains我不需要傳遞所有變量,只需要那些將被“懲罰”的變量。 c_f_pointer調用應該在有問題的調用之前完成,因為索引變量(示例中的idx )可以在公共塊中,並在其他調用中更改,例如。

除了原始代碼中已有的陷阱之外,還有任何陷阱嗎?

英特爾Fortran支持!dec$ attributes no_arg_check指令 它指示編譯器“忽略與顯式接口相關的類型和形狀匹配規則”

“它可以應用於單個偽參數名稱或例程名稱,在這種情況下,該選項將應用於該接口中的所有偽參數。”

它應該應用於模塊過程(或接口塊),因此您應該將函數和子例程移動到模塊中。

許多其他編譯器也有類似的指令


您的代碼有什么問題? 根據經驗, 不要使用任何返回pointer的Fortran函數 他們是純粹的邪惡。 Fortran指針與C指針完全不同。

當你call fill_int(cast_to_int(a),10)時會發生什么,表達式cast_to_int(a)被計算,結果是一個數組。 現在,根據優化,編譯器可以選擇傳遞原始指針的地址,但它也可以創建結果整數數組的副本並將副本傳遞給子例程。

此外,您的數組a沒有target屬性,因此cast_to_int(a)使用的地址僅在函數內有效,並且在返回后無效。

您應該在主程序中創建b並且只傳遞b而不是a 它的工作方式類似於等價。 查看存儲為不同類型的值無論如何都不符合標准。 這種形式的雙關語是不允許的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM