如何将字符串数组从C和Fortran传递到Fortran

How to pass arrays of strings from both C and Fortran to Fortran?

本文关键字:Fortran 字符串 数组      更新时间:2023-10-16

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

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

我现在有一个C例程,我想从传递字符串数组(string)

#include <iostream>
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字符,因此您正在读取一些垃圾(缓冲区溢出)。当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