我想将一个字符串从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
但是这样我得到了一些奇怪的符号。
我想做正确的事情所以我以后不用担心。内存泄漏不是我想要的。所以我想我会尝试你建议的替代方式。我想我想知道我怎么知道我是否需要释放一个指针。这是我在StackOverflow上找到的另一个代码(尽管这个代码将C ++字符串传递给Fortran。https://stackoverflow.com/a/30430656/721644)
您认为这可以使用吗?或者可能存在内存泄漏。您也可以给我一些关于您建议的替代方式的提示吗?
答案 0 :(得分:4)
在许多其他问题中处理了这种变体。搜索关闭的确切副本可能是没有希望的,但我强烈建议您搜索并阅读这些问题和答案。
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
要修剪字符串,只需将空字符放在正确的位置
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结尾的缓冲区。可以在http://fortranwiki.org/fortran/show/c_interface_module
找到c_strlen
要符合100%标准,你应该使用长度为1的字符数组character(kind=c_char)
,但字符串很可能在实践中有效。
答案 1 :(得分: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;
}