将字符串从 Fortran 传递到 C/C++ 的正确方法

Ban*_*hen 5 c++ string fortran fortran-iso-c-binding

我想将一个字符串从 Fortran 传递给 C/C++。这是我的 Fortran 代码:

subroutine zdplaskinGetSpeciesName(cstring, index) bind(C, name='zdplaskinGetSpeciesName')
  use iso_c_binding
  use ZDPlasKin
  implicit none
  integer, intent(in) :: index
  CHARACTER(10), TARGET :: fstring = ''
  TYPE(C_PTR) :: cstring
  fstring = species_name(index+1)
  cstring = c_loc(fstring)
end subroutine zdplaskinGetSpeciesName
Run Code Online (Sandbox Code Playgroud)

ZDPlasKin是一个具有species_name(i).

extern "C" void zdplaskinGetSpeciesName(char* cstring[], size_t* index);
char* cstring[1];
size_t index = 1;
zdplaskinGetSpeciesName(cstring, &index);
string speciesName = string(cstring[0]);
cout << speciesName << endl;
Run Code Online (Sandbox Code Playgroud)

这种方法的输出似乎很好。但是,我想修剪尾随空格 ( character(10) 以提供额外空间),因此我的 C++ 代码可以正确读取字符串。我尝试了另一种方式。

subroutine zdplaskinGetSpeciesName(cstring, index) bind(C, name='zdplaskinGetSpeciesName')
  use iso_c_binding
  use ZDPlasKin
  implicit none
  integer, intent(in) :: index
  CHARACTER(:), allocatable, TARGET :: fstring
  TYPE(C_PTR) :: cstring
  fstring = trim(species_name(index+1))
  cstring = c_loc(fstring)
end subroutine zdplaskinGetSpeciesName
Run Code Online (Sandbox Code Playgroud)

但是这样我得到了一些奇怪的符号。


我想正确地做事,这样我以后就不用担心了。内存泄漏不是我想要的。所以我想我会尝试你建议的替代方式。我想我想知道的是我怎么知道我是否需要释放一个指针。这里是另一个代码我在计算器上发现(虽然这一个通过C ++字符串的Fortran。/sf/answers/2130145951/

你觉得这个好用吗?或者可能存在内存泄漏。你也可以给我一些关于你建议的替代方式的提示吗?

Vla*_*r F 5

在许多其他问题中处理了这种变体。搜索完全重复的关闭可能是无望的,但我强烈建议您搜索并阅读这些问题和答案。

C 字符串以空字符结尾。如果要修剪它,只需将空字符放在正确的位置即可。实际上,您在第一个版本中根本就不是空终止字符串,因此很容易发生缓冲区溢出。

但更糟糕的是,变量fstring只是函数的局部变量。永远不要传递指向局部变量的指针。

所以,第一个版本真的应该是

subroutine zdplaskinGetSpeciesName(cstring, index) bind(C, name='zdplaskinGetSpeciesName')
  use iso_c_binding
  use ZDPlasKin
  implicit none
  integer, intent(in) :: index
  CHARACTER(11), POINTER :: fstring
  TYPE(C_PTR) :: cstring
  allocate(fstring)
  fstring = species_name(index+1)
  fstring(11:11) = c_null_char
  cstring = c_loc(fstring)
end subroutine zdplaskinGetSpeciesName
Run Code Online (Sandbox Code Playgroud)

要修剪字符串,您只需将空字符放在正确的位置

   integer :: len
   ...
   len = len_trim(fstring)
   fstring(len+1:len+1) = c_null_char
Run Code Online (Sandbox Code Playgroud)

您还可以使用指向长度匹配字符串长度 + 1 的字符数组的character(:)指针或类似于第二种方法的延迟长度 ( ) 字符指针。请记住始终为空终止保留 1 个字符。

现在代码会有内存泄漏。为了避免内存泄漏,必须从 Fortran 中释放字符串!所以你必须创建一个特殊的子程序来释放这些指针。


另一种方法是从 C++ 传递一个指向缓冲区的指针,然后在 Fortran 中填充字符串。我实际上更喜欢那样。您可以在 C++ 中让它以空字符结尾,只是确保不要让 Fortran 覆盖终止字符。

你可以试试:

subroutine zdplaskinGetSpeciesName(cstring, index) bind(C, name='zdplaskinGetSpeciesName')
  use iso_c_binding
  use ZDPlasKin
  implicit none
  integer, intent(in) :: index
  TYPE(C_PTR), intent(in) :: cstring
  CHARACTER(kind=C_CHAR), POINTER :: fstring(:)
  integer :: i, c_len, name_len

  c_len = c_strlen(cstring)
  c_f_pointer(cstring, fstring, [c_len])

  associate(name => species_name(index+1))
    name_len = trim_len(name)
    do i = 1, name_len
      fstring(i) = name(i:i)
    end do
    fstring(name_len+1:name_len+1)
  end do
end subroutine zdplaskinGetSpeciesName
Run Code Online (Sandbox Code Playgroud)

未经测试。您有责任从 C++ 提供足够大的空终止缓冲区。c_strlen可以在http://fortranwiki.org/fortran/show/c_interface_module找到


要 100% 符合标准,您应该使用character(kind=c_char)长度为 1的字符数组,但字符串很可能在实践中起作用。