首页 文章

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

提问于
浏览
4

如何将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测试了上述内容 . lengths of the strings is fixed (上例中为3),以防这简化了解决方案 . 但是,阵列的尺寸可以变化 .

任何指针(甚至C指针)都将非常感激 .

2 回答

  • 1

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

    有时候解决方案比预期的要容易 . 原来, 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?,因为在那里我实现了 c_f_pointer(cptr, fptr[, shape]) 的可选 shape 参数 .

相关问题