繁体   English   中英

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

[英]The correct way to pass a string from Fortran to C/C++

我想将一个字符串从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

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;

对于这种方法,输出似乎没问题。 但是,我想修剪尾随空格( 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

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


我想做正确的事情所以我以后不用担心。 内存泄漏不是我想要的。 所以我想我会尝试你建议的替代方式。 我想我想知道我怎么知道我是否需要释放一个指针。 这里是另一个代码我在计算器上发现(虽然这一个通过C ++字符串的Fortran。 https://stackoverflow.com/a/30430656/721644

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

在许多其他问题中处理了这种变体。 搜索关闭的确切副本可能是没有希望的,但我强烈建议您搜索并阅读这些问题和答案。

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

要修剪字符串,只需将null char放在正确的位置即可

   integer :: len
   ...
   len = len_trim(fstring)
   fstring(len+1:len+1) = c_null_char

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

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


另一种方法是从C ++传递指向缓冲区的指针,然后只填充Fortran中的字符串。 我实际上更喜欢这种方式。 你可以在C ++中使它以null结尾,只是一定不要让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

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


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

我终于有了一个好方法。 首先,下载http://fortranwiki.org/fortran/show/c_interface_module 并使用F_C_string_ptr。

subroutine zdplaskinGetSpeciesName(cstring, index) bind(C, name='zdplaskinGetSpeciesName')
  use ZDPlasKin
  use C_interface_module
  implicit none
  integer, intent(in) :: index
  TYPE(C_PTR), intent(in) :: cstring
  character(:), allocatable :: fstring
  fstring = trim(species_name(index+1))
  call F_C_string_ptr(fstring, cstring)
end subroutine zdplaskinGetSpeciesName

而c和c ++代码是:

extern "C" void zdplaskinGetSpeciesName(char* cstring[], size_t* index);
for (size_t i = 0; i < zdplaskinNSpecies(); i++) {
    char* cstring[10];
    zdplaskinGetSpeciesName(cstring, &i);
    printf("%s\n",*cstring);
    string speciesName(*cstring);
    cout << speciesName << endl;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM