5

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

我已经阅读了一些相关的问题和答案。有些(即thisthis)只是“使用 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 的字符串数组?

4

3 回答 3

5

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

特别是,您还将一个维度为 3 的 character(4) 数组传递给一个需要更多数据的子例程(255 个字符,尽管我不知道索引顺序)。只传递了指针,所以我认为它无法检查。

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

[character(255) :: "a","ab","abc"]
于 2012-12-13T22:43:34.893 回答
3

我实际上看到了两种方法来做到这一点。或者,您使用 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 字符类型兼容。

于 2012-12-14T14:28:18.870 回答
1

我想出的一种方法是修改调用 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
于 2012-12-14T15:21:12.153 回答