【问题标题】:Passing an array of C-strings to Fortran (iso_c_binding)将 C 字符串数组传递给 Fortran (iso_c_binding)
【发布时间】:2014-10-25 22:36:51
【问题描述】:

如何将 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 和 Intel Fortran,并已使用 GNU Fortran 测试了上述内容。 字符串的长度是固定的(上例中为 3),以防这简化了解决方案。但是,数组的维度可以变化。

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

【问题讨论】:

  • 请注意:char* cstrings[] 是一个 pointer 指向“字符串”的数组,它不一定指向一个连续的内存块。看看这里:stackoverflow.com/q/13869528/694576 并注意“字符串”数组的定义方式不同。
  • 谢谢@alk。这可能是这里的问题。我的C技能不是很好。我怎样才能让它连续?
  • "我如何让它连续?" 就像我链接的问题中的代码那样做。
  • 此类问题的谷歌关键字是“混合语言编程”。
  • 感谢@alk,您最初的评论中没有该链接。事实上,这个答案看起来很有希望。现在我正在努力掌握细节。

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


【解决方案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);
}

【讨论】:

  • 感谢您的回答。但是,我认为对 c_f_pointer 的调用在您的示例中起作用纯属运气(或依赖于编译器)。您肯定需要来自c_loc 的 C 内存位置。参见例如c_f_pointer (Intel Fortran)
  • @alexurba 不,没关系。我将type(C_PTR) 传递给c_f_pointer,这是正确的类型。您的参考证实了这一点,尽管我非常了解c_f_pointer
  • 好的,现在我明白了。感谢@Vladimir 的澄清!
  • @VladimirF - 我尝试传递一维 char * : char *cstring = {"hello world"};,(跳过 cstring 声明中的 dimension(n) 以及 do-loop)并仅使用 @987654334 @ 但我在 fstring 中得到的只是 lengthofcstring (=11) 未定义的指针。为什么 c_f_pointer = 4 中的最后一个参数?不应该是3吗?
  • 是的,3,但是 4 也可以,第四个空字符被 C 附加并且不打印。
【解决方案2】:

有时解决方案比预期的要容易。事实证明,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 参数。

【讨论】:

  • 这看起来太复杂了,atom_types 是什么?你没有在任何地方声明它。
  • 通过调用c_loc 你实际上是指向一个指针。一旦您的字符串数组不连续,即动态分配单个字符串时,您的方法就会失败。
  • 抱歉atom_types - 这显然是一个错字。我现在明白我不需要c_loc。您的解决方案是正确的。谢谢,@Vladimir。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-15
  • 1970-01-01
  • 2015-09-09
相关资源
最近更新 更多