将一个C字符串数组传递给Fortran(iso_c_binding)

时间:2014-09-01 16:57:01

标签: c string fortran fortran-iso-c-binding

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

问题Arrays of strings in fortran-C bridges using iso_c_binding肯定是相关的,但答案似乎不正确,甚至不能用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和英特尔Fortran,并使用GNU Fortran测试了上述内容。字符串的长度是固定的(在上例中为3),以防这简化了解决方案。但是,阵列的尺寸可能会有所不同。

任何指针(甚至是C指针)都会非常感激。

2 个答案:

答案 0 :(得分:5)

问题代码中最大的问题是您使用的是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);
}

答案 1 :(得分: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指向How to pass arrays of strings from both C and Fortran to Fortran?,因为我意识到shape的可选c_f_pointer(cptr, fptr[, shape])参数。