5

如何将 C 字符串 ( char* cstrings[]) 数组传递给 Fortran 子例程?

使用iso_c_binding的 fortran-C 桥中的字符串数组肯定是相关的,但答案似乎不正确,甚至无法使用 GNU Fortran 编译。

我目前正在为 Fortran 代码开发一个 C 接口,我希望iso_c_binding(我以前使用过)可以让这变得轻而易举。到目前为止,C 字符串数组还没有运气......

Fortran 子例程应将字符串数组作为参数。在普通的 Fortran 中,我会写如下内容:

subroutine print_fstring_array(fstring)

  implicit none

  character(len=*), dimension(:), intent(in) :: fstring
  integer                                    :: i

  do i = 1, size(fstring)
    write(*,*) trim(fstring(i))
  end do

end subroutine print_fstring_array

将单个 C 字符串传递给 Fortran 的一种方法是作为 C 指针 ( c_ptr) (我知道,我也可以使用 的数组character(kind=c_char)

subroutine print_cstring(cstring) bind(C)

  use iso_c_binding, only: c_ptr, c_f_pointer, c_loc, c_null_char
  implicit none

  type(c_ptr), target, intent(in) :: cstring
  character(len=1024), pointer    :: fstring
  integer                         :: slen

  call c_f_pointer(c_loc(cstring), fstring)
  slen = index(fstring, c_null_char) - 1
  write(*,*) fstring(1:slen)

end subroutine print_cstring

所以,我认为数组c_ptr是个好主意

subroutine print_cstring_array(n, cstring) bind(C)

  use iso_c_binding, only: c_ptr, c_int, c_f_pointer, c_loc, c_null_char
  implicit none

  integer(kind=c_int),               intent(in) :: n
  type(c_ptr), dimension(n), target, intent(in) :: cstring
  character(len=1024), pointer                  :: fstr
  integer                                       :: slen, i

  do i = 1, n
    call c_f_pointer(c_loc(cstring(i)), fstring)
    slen = index(fstring, c_null_char) - 1
    write(*,*) fstring(1:slen)
  end do

end subroutine print_cstring_array

但这会产生分段错误。

最后一个示例的 C 代码是

# include "stdio.h"
# include "fstring.h"

void main(void) {
  char* cstring[] = { "abc", "def", "ghi", "jkl" };
  int n = 4;
  print_cstring_array(&n, cstring);
}

头文件的内容fstring.h很简单:

void print_cstring_array(int* n, char* cstring[]);

我的目标是 GNU Fortran 和 Intel Fortran,并用 GNU Fortran 测试了上述内容。字符串的长度是固定的(上例中为 3),以防这简化了解决方案。但是,数组的维度可以变化。

任何指针(甚至 C 指针)都将不胜感激。

4

2 回答 2

8

问题中代码中最大的问题是您使用c_f_pointer(c_loc(cstring),而不是c_f_pointer(cstring,.

这对我有用:

subroutine print_cstring_array(n, cstring) bind(C)

  use iso_c_binding, only: c_ptr, c_int, c_f_pointer, c_loc, c_null_char
  implicit none

  integer(kind=c_int),               intent(in) :: n
  type(c_ptr), dimension(n), target, intent(in) :: cstring
  character, pointer                            :: fstring(:)
  integer                                       :: slen, i

  do i = 1, n
    call c_f_pointer(cstring(i), fstring, [4])
    write(*,*) fstring
  end do

end subroutine print_cstring_array



# include "stdio.h"

void print_cstring_array(int* n, char* cstring[]);

void main(void) {
  char* cstring[] = { "abc", "def", "ghi", "jkl" };
  int n = 4;
  print_cstring_array(&n, cstring);
}
于 2014-09-01T18:23:12.007 回答
1

有时解决方案比预期的要容易。事实证明,c_f_pointer(cptr, fptr[, shape])将数组形状作为可选参数来转换 C 指针数组(我在参考资料中错过了):

subroutine print_cstring_array(n, cstring) bind(C)

  use iso_c_binding, only: c_ptr, c_int, c_f_pointer, c_loc, c_null_char
  implicit none

  integer(kind=c_int),                 intent(in) :: n
  type(c_ptr), target,                 intent(in) :: cstring
  character(kind=c_char), dimension(:,:), pointer :: fptr
  character(len=3), dimension(n)                  :: fstring

  call c_f_pointer(c_loc(cstring), fptr, [3, n])
  do i = 1, n
     slen = 0
     do while(fptr(slen+1,i) /= c_null_char)
        slen = slen + 1
     end do
     fstring(i) = transfer(fptr(1:slen,i), fstring(i))
     write(*,*) trim(fstring(i))
  end do                                                

end subroutine print_cstring_array

感谢@alk 向我指出如何将字符串数组从 C 和 Fortran 传递到 Fortran?,因为在那里我实现了 的可选shape参数c_f_pointer(cptr, fptr[, shape])

于 2014-09-01T18:44:13.060 回答