如何将C和Fortran的字符串数组传递给Fortran?

我试图将一个字符串数组从C传递给Fortran子程序以及从Fortran传递到同一个Fortran子程序。 我已成功地从C和Fortran传递单个字符串(即1D字符数组)。 但是,我遇到了字符串数组的问题。 我在Fortran端使用ISO C绑定,理想情况下我希望它在呼叫方面尽可能无缝。

我已经阅读了一些相关的问题和答案。 一些(即这个和这个 )只是“使用ISO C”而没有进一步的细节,这没有多大帮助。 这个答案非常有用(对不同问题的类似答案),但仅适用于单个字符串,其中似乎在单个Fortran字符串中识别c_null_char。 如果没有两个单独的例程,我无法弄清楚如何处理数组的情况。

我目前拥有的是一个C例程,我想传递字符串数组( string ):

 #include  extern "C" void print_hi_array(char input_string[][255]); using namespace std; int main() { char string[3][255] = {"asdf","ghji","zxcv"}; print_hi_array(string); return 0; } 

并且,类似的Fortran例程:

 program main implicit none call print_hi_array( (/"asdf", "ghji", "zxcv"/) ) end program 

到目前为止,这就是我对接收端的看法:

 subroutine print_hi_array(input_string) bind(C) use iso_c_binding, only: C_CHAR, c_null_char implicit none character (kind=c_char, len=1), dimension (3,255), intent (in) :: input_string character (len=255), dimension (3) :: regular_string character (len=255) :: dummy_string integer :: i,j,k write (*,*) input_string do j = 1 , 3 dummy_string(:) = c_null_char k = 1 do i = 1 + (j-1)*255, j*255,1 if (input_string(i) .ne. c_null_char) then write (*,*) "i ",i,j, input_string(i) dummy_string(k:k) = input_string(i) endif k = k +1 enddo regular_string(j) = dummy_string enddo write (*,*) regular_string end subroutine print_hi_array 

这适用于C函数; 我得到这个输出:

  asdfghjizxcv j= 1 i 1 1 a i 2 1 s i 3 1 d i 4 1 f j= 2 i 256 2 g i 257 2 h i 258 2 j i 259 2 i j= 3 i 511 3 z i 512 3 x i 513 3 c i 514 3 v asdf ghji zxcv 

然而,当它通过Fortran完成时,我得到了废话:

 asdfghjizxcv@O,B @(P,B ]B]6(P,B @ ....... 

这种方法似乎没有c_null_char

那么,我如何编写一个Fortran子例程来接收来自C和Fortran的字符串数组?

如果声明的长度超过其存储的文本,则Fortran使用空格填充字符串的其余部分。 它不是零分隔的,声明的长度存储在隐藏变量中。 它不包含c null char,因此您正在读取一些垃圾(缓冲区溢出)。 当tlit打印带有\ 000的字符串时,Fortran应该打印什么是标准未定义的,取决于实现。

特别是,您还将具有维度3的字符(4)数组传递给期望更多数据的子例程(255个字符,尽管我对索引顺序不感兴趣)。 只传递指针,所以我认为无法检查。

可以通过这种方式在数组构造函数中定义字符串的长度:

 [character(255) :: "a","ab","abc"] 

我实际上看到了两种方法。 或者,你在C中编写一个循环并将字符串逐个传递给Fortran,就像你之前已经做过的那样。 或者,如果要传递整个数组, 并且希望使用相同的例程处理Fortran和C数组,则必须为C字符串数组制作适当的副本。 下面是一个有效的但没有太多测试的例子:

 extern "C" void print_array_c(int nstring, char input_string[][255]); using namespace std; int main() { char string[3][255] = {"asdf","ghji","zxcv"}; print_array_c(3, string); return 0; } 

请注意,我还传递了字符串的数量,以便该示例可以处理各种大小的数组。 (但是,字符串的长度假定为255个字符。)在Fortran大小上,需要一个例程来转换Fortran字符串。 一种可能的可视化可能是:

 module arrayprint_module use, intrinsic :: iso_c_binding implicit none integer, parameter :: STRLEN = 255 contains !> The printing routine, works with Fortran character arrays only. subroutine print_array(strings) character(len=STRLEN), intent(in) :: strings(:) integer :: ii do ii = 1, size(strings) write(*,*) ii, strings(ii) end do end subroutine print_array !> Converts C string array to Fortran string array and invokes print_array. subroutine print_array_c(nstring, cptr) bind(C) integer(c_int), value :: nstring type(c_ptr), intent(in), value :: cptr character(kind=c_char), pointer :: fptr(:,:) character(STRLEN), allocatable :: fstrings(:) integer :: ii, lenstr call c_f_pointer(cptr, fptr, [ STRLEN, nstring ]) allocate(fstrings(nstring)) do ii = 1, nstring lenstr = cstrlen(fptr(:,ii)) fstrings(ii) = transfer(fptr(1:lenstr,ii), fstrings(ii)) end do call print_array(fstrings) end subroutine print_array_c !> Calculates the length of a C string. function cstrlen(carray) result(res) character(kind=c_char), intent(in) :: carray(:) integer :: res integer :: ii do ii = 1, size(carray) if (carray(ii) == c_null_char) then res = ii - 1 return end if end do res = ii end function cstrlen end module arrayprint_module 

请注意,从C传递的数组必须是连续的才能工作,我认为字符(kind = c_char)与fortran字符类型兼容,通常应该是这样。

我提出的一种方法是修改调用Fortran例程以使用ISO C绑定:

 program main use iso_c_binding, only: C_CHAR implicit none character (kind=c_char, len=255), dimension (3) :: input_string input_string = (/ "asdf", "ghji", "zxcv" /) call print_hi_array(input_string) end program