Fortran 和 C 互操作性:将 char *string 作为函数的参数传递给 Fortran
Fortran and C interoperability: passing char *string as a function's argument to Fortran
我想创建一个 Fortran 绑定到 C 函数,returns 一个任意长度的字符串作为函数参数。我已阅读 this similar question,但我根本无法使解决方案在那里工作。我已经尽可能地简化了问题,所以我可以在这里解释。
我有一个这样的 C 函数:c_string.c(我知道通常这种函数也会有一个参数,该参数也是 returns 字符串的长度,但我已经离开了为简单起见,在这里列出,因为在这个例子中我知道长度。)
#include <string.h>
void make_string(char *string) {
strcpy(string, "sand");
}
它似乎在 C 程序中运行良好,例如:
#include <stdlib.h>
#include <stdio.h>
#include "c_string.c"
int main() {
char *string;
string = malloc(5*sizeof(char));
make_string(string);
printf(string);
}
我想在 Fortran 程序中调用这个函数:main.f90。所以我按照上面提到的解决方案进行了c绑定。
program main
use iso_c_binding
implicit none
interface
subroutine c_make_string(string) bind (C, name='make_string')
import
type (c_ptr), value :: string
end subroutine c_make_string
end interface
type (c_ptr) :: c_string
character, pointer :: local_string(:)
call c_make_string(c_string)
call c_f_pointer(c_string, local_string, [4])
write (*,*) local_string
end program main
但是这个程序的结果是写语句处的段错误。尝试通过索引访问生成的 local_string
指针也会产生段错误。
注意:在接口中添加value属性后,段错误现在在C函数中。
我想到的唯一解决方案是不使用 c_ptr
和 c_f_pointer()
,而只使用最大缓冲区长度,如 512。
program main
use iso_c_binding
implicit none
interface
subroutine c_make_string(string) bind (C, name='make_string')
import
character (len=1, kind=c_char) :: string(512)
end subroutine
end interface
character (len=1, kind=c_char) :: local_string(512)
call c_make_string(local_string)
write (*,*) local_string
end program main
但这会导致在字符串中的有用数据之后出现一大堆垃圾。当然,如果您知道预期字符串的长度,这很容易解决,但我觉得这并不令人满意……要是我知道原始解决方案中的 c_f_pointer()
调用出了什么问题就好了。我应该像在 C 程序中那样为 local_string
分配 space 吗?但是怎么办?为什么我似乎需要固定长度的数组来进行绑定?通过 subroutine/function 参数传递任意长度的 char * 变量的干净解决方案是什么?
您没有在任何地方为字符串分配内存。您只是将一个未定义的指针传递给 C 函数,然后 C 函数尝试将一些字符数据复制到这个未定义的地址。
要么 pre-allocate Fortran 中的字符串,将其长度传递给 C 并使用 strncpy
或 malloc
C 中的字符串,但随后您必须将其从 C 中释放出来。
例如:
#include <string.h>
void copy_string(char *string, int n) {
strncpy(string, "sand", n);
}
program main
use iso_c_binding
implicit none
interface
subroutine c_copy_string(string, n) bind (C, name='copy_string')
import
type (c_ptr), value :: string
integer(c_int), value :: n
end subroutine
end interface
type (c_ptr) :: c_string
character(c_char), pointer :: local_string(:)
allocate(local_string(10))
c_string = c_loc(local_string)
call c_copy_string(c_string, int(size(local_string), c_int))
write (*,*) local_string
deallocate(local_string)
end program main
> gfortran -fsanitize=address,undefined make_string.c main.f90
> ./a.out
sand
如果你真的想在 C 中“制作”字符串,你必须为它制作 malloc space。在这种情况下,您需要通过引用传递地址。然后你要么也必须传递长度或制作一个 c_strlen
函数来找出 null-delimited C 字符串有多大(来自 fortranwiki 的 or here or here). There are examples of that already existing on the internet so it is not very useful to write all that from scratch here. The c_interface_module 在这里可能非常有帮助,如果没有,它展示了如何在 C 和 Fortran 之间来回传递字符串的好例子。
请记住,如果字符串在 C 中被分配,则必须从 C 中删除它。
我想创建一个 Fortran 绑定到 C 函数,returns 一个任意长度的字符串作为函数参数。我已阅读 this similar question,但我根本无法使解决方案在那里工作。我已经尽可能地简化了问题,所以我可以在这里解释。
我有一个这样的 C 函数:c_string.c(我知道通常这种函数也会有一个参数,该参数也是 returns 字符串的长度,但我已经离开了为简单起见,在这里列出,因为在这个例子中我知道长度。)
#include <string.h>
void make_string(char *string) {
strcpy(string, "sand");
}
它似乎在 C 程序中运行良好,例如:
#include <stdlib.h>
#include <stdio.h>
#include "c_string.c"
int main() {
char *string;
string = malloc(5*sizeof(char));
make_string(string);
printf(string);
}
我想在 Fortran 程序中调用这个函数:main.f90。所以我按照上面提到的解决方案进行了c绑定。
program main
use iso_c_binding
implicit none
interface
subroutine c_make_string(string) bind (C, name='make_string')
import
type (c_ptr), value :: string
end subroutine c_make_string
end interface
type (c_ptr) :: c_string
character, pointer :: local_string(:)
call c_make_string(c_string)
call c_f_pointer(c_string, local_string, [4])
write (*,*) local_string
end program main
但是这个程序的结果是写语句处的段错误。尝试通过索引访问生成的 local_string
指针也会产生段错误。
注意:在接口中添加value属性后,段错误现在在C函数中。
我想到的唯一解决方案是不使用 c_ptr
和 c_f_pointer()
,而只使用最大缓冲区长度,如 512。
program main
use iso_c_binding
implicit none
interface
subroutine c_make_string(string) bind (C, name='make_string')
import
character (len=1, kind=c_char) :: string(512)
end subroutine
end interface
character (len=1, kind=c_char) :: local_string(512)
call c_make_string(local_string)
write (*,*) local_string
end program main
但这会导致在字符串中的有用数据之后出现一大堆垃圾。当然,如果您知道预期字符串的长度,这很容易解决,但我觉得这并不令人满意……要是我知道原始解决方案中的 c_f_pointer()
调用出了什么问题就好了。我应该像在 C 程序中那样为 local_string
分配 space 吗?但是怎么办?为什么我似乎需要固定长度的数组来进行绑定?通过 subroutine/function 参数传递任意长度的 char * 变量的干净解决方案是什么?
您没有在任何地方为字符串分配内存。您只是将一个未定义的指针传递给 C 函数,然后 C 函数尝试将一些字符数据复制到这个未定义的地址。
要么 pre-allocate Fortran 中的字符串,将其长度传递给 C 并使用 strncpy
或 malloc
C 中的字符串,但随后您必须将其从 C 中释放出来。
例如:
#include <string.h>
void copy_string(char *string, int n) {
strncpy(string, "sand", n);
}
program main
use iso_c_binding
implicit none
interface
subroutine c_copy_string(string, n) bind (C, name='copy_string')
import
type (c_ptr), value :: string
integer(c_int), value :: n
end subroutine
end interface
type (c_ptr) :: c_string
character(c_char), pointer :: local_string(:)
allocate(local_string(10))
c_string = c_loc(local_string)
call c_copy_string(c_string, int(size(local_string), c_int))
write (*,*) local_string
deallocate(local_string)
end program main
> gfortran -fsanitize=address,undefined make_string.c main.f90
> ./a.out
sand
如果你真的想在 C 中“制作”字符串,你必须为它制作 malloc space。在这种情况下,您需要通过引用传递地址。然后你要么也必须传递长度或制作一个 c_strlen
函数来找出 null-delimited C 字符串有多大(来自 fortranwiki 的
请记住,如果字符串在 C 中被分配,则必须从 C 中删除它。