簡體   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