2017-07-26 144 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'”。* –

相关问题