将字符串从Fortran传递到C/C++的正确方法
我想从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)
你认为这样可以使用吗?或者可能有内存泄漏。你也可以给我一些关于你建议的替代方法的暗示吗?
这个变体在其他许多问题中都被处理过。搜索一个确切的重复封闭可能是无望的,但我强烈建议你搜索和阅读这些问题和答案。
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)
的阵列,但字符串是非常有可能在实践工作。
谢谢你的回答。我想正确地做事,所以我不需要担心以后。内存泄漏不是我想要的。所以我想我会尝试你所建议的替代方式。我想我想知道是如何知道我是否需要释放指针。这里是我在stackoverflow上找到的另一个代码: –
你不需要在这里粘贴来自其他SO问题的代码,链接就足够了。但是你想用这些代码做什么?它做了一些不同的事。 –
我终于找到了一个好方法。首先,下载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;
}
对fstring的分配是完全不必要的。你可以完全删除'fstring'变量。 '调用F_C_string_ptr(trim(species_name(index + 1)),cstring)'另外,你的'cstring [10]'可能太短并且期望缓冲区溢出。 –
*“如果长度未通过,则C字符串必须至少为:'len(F_string)+ 1'”。* –
*可是这样我得到了一些奇怪的符号*你得到了什么? –
请注意,没有语言C/C++和一些在这里做C和C++的人对此非常过敏。 Fortranners通常不关心。我知道它通过C互操作性来调用C++,所以我并不反对这两种标签,但许多其他人都是。 –
这两个版本都无法工作。子例程退出时,本地可分配字符串将被释放。局部变量仅在执行期间在堆栈上有效。 –