2017-07-26 118 views
4

我想從Fortran傳遞一個字符串到C/C++。這裏是我的Fortran代碼:將字符串從Fortran傳遞到C/C++的正確方法

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 

但是這樣我得到了一些奇怪的符號。


我想正確地做事情,所以我不需要擔心以後。內存泄漏不是我想要的。所以我想我會嘗試你所建議的替代方式。我想我想知道是如何知道我是否需要釋放指針。這裏是我在StackOverflow上找到的另一個代碼(雖然這個代碼將C++字符串傳遞給了Fortran,但是這個代碼是https://stackoverflow.com/a/30430656/721644

你認爲這樣可以使用嗎?或者可能有內存泄漏。你也可以給我一些關於你建議的替代方法的暗示嗎?

+0

*可是這樣我得到了一些奇怪的符號*你得到了什麼? –

+2

請注意,沒有語言C/C++和一些在這裏做C和C++的人對此非常過敏。 Fortranners通常不關心。我知道它通過C互操作性來調用C++,所以我並不反對這兩種標籤,但許多其他人都是。 –

+0

這兩個版本都無法工作。子例程退出時,本地可分配字符串將被釋放。局部變量僅在執行期間在堆棧上有效。 –

回答

4

這個變體在其他許多問題中都被處理過。搜索一個確切的重複封閉可能是無望的,但我強烈建議你搜索和閱讀這些問題和答案。

C字符串以空字符結尾。如果你想修剪它,你只需將空字符放在正確的位置。實際上,你不是null在你的第一個版本中終止字符串,所以它很容易發生緩衝區溢出。

但更糟糕的是,變量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 

要修剪字符串中,你只需將空字符到正確的位置

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

你也可以使用一個指向一個字符數組長度匹配字符串+ 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 

未經檢驗。您有責任從C++提供足夠大的以null結尾的緩衝區。 c_strlen可以在http://fortranwiki.org/fortran/show/c_interface_module


被認爲是100%符合標準的,你應該使用字符長度爲一,character(kind=c_char)的陣列,但字符串是非常有可能在實踐工作。

+0

謝謝你的回答。我想正確地做事,所以我不需要擔心以後。內存泄漏不是我想要的。所以我想我會嘗試你所建議的替代方式。我想我想知道是如何知道我是否需要釋放指針。這裏是我在stackoverflow上找到的另一個代碼: –

+0

你不需要在這裏粘貼來自其他SO問題的代碼,鏈接就足夠了。但是你想用這些代碼做什麼?它做了一些不同的事。 –

0

我終於找到了一個好方法。首先,下載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; 
} 
+0

對fstring的分配是完全不必要的。你可以完全刪除'fstring'變量。 '調用F_C_string_ptr(trim(species_name(index + 1)),cstring)'另外,你的'cstring [10]'可能太短並且期望緩衝區溢出。 –

+0

*「如果長度未通過,則C字符串必須至少爲:'len(F_string)+ 1'」。* –

相關問題