如何将C字符串数组(char* cstrings[]
)传递给Fortran
子程序?
问题Arrays of strings in fortran-C bridges using iso_c_binding肯定是相关的,但答案似乎不正确,甚至不能用GNU Fortran编译。
我目前正在为Fortran代码开发一个C接口,我期望iso_c_binding
(我之前使用过)会让这件事变得轻而易举。到目前为止C字符串数组没有运气......
Fortran子例程应该将一个字符串数组作为参数。 在普通的Fortran中,我会写如下内容:
subroutine print_fstring_array(fstring)
implicit none
character(len=*), dimension(:), intent(in) :: fstring
integer :: i
do i = 1, size(fstring)
write(*,*) trim(fstring(i))
end do
end subroutine print_fstring_array
将单个C字符串传递给Fortran的一种方法是作为C指针(c_ptr
)(我知道,我也可以使用character(kind=c_char)
数组)
subroutine print_cstring(cstring) bind(C)
use iso_c_binding, only: c_ptr, c_f_pointer, c_loc, c_null_char
implicit none
type(c_ptr), target, intent(in) :: cstring
character(len=1024), pointer :: fstring
integer :: slen
call c_f_pointer(c_loc(cstring), fstring)
slen = index(fstring, c_null_char) - 1
write(*,*) fstring(1:slen)
end subroutine print_cstring
所以,我假设一个c_ptr
数组是个好主意
subroutine print_cstring_array(n, cstring) bind(C)
use iso_c_binding, only: c_ptr, c_int, c_f_pointer, c_loc, c_null_char
implicit none
integer(kind=c_int), intent(in) :: n
type(c_ptr), dimension(n), target, intent(in) :: cstring
character(len=1024), pointer :: fstr
integer :: slen, i
do i = 1, n
call c_f_pointer(c_loc(cstring(i)), fstring)
slen = index(fstring, c_null_char) - 1
write(*,*) fstring(1:slen)
end do
end subroutine print_cstring_array
但这会产生分段错误。
最后一个例子的C代码是
# include "stdio.h"
# include "fstring.h"
void main(void) {
char* cstring[] = { "abc", "def", "ghi", "jkl" };
int n = 4;
print_cstring_array(&n, cstring);
}
并且头文件fstring.h
的内容只是:
void print_cstring_array(int* n, char* cstring[]);
我的目标是GNU Fortran和英特尔Fortran,并使用GNU Fortran测试了上述内容。字符串的长度是固定的(在上例中为3),以防这简化了解决方案。但是,阵列的尺寸可能会有所不同。
任何指针(甚至是C指针)都会非常感激。
答案 0 :(得分:5)
问题代码中最大的问题是您使用的是c_f_pointer(c_loc(cstring),
而不是c_f_pointer(cstring,
。
这对我有用:
subroutine print_cstring_array(n, cstring) bind(C)
use iso_c_binding, only: c_ptr, c_int, c_f_pointer, c_loc, c_null_char
implicit none
integer(kind=c_int), intent(in) :: n
type(c_ptr), dimension(n), target, intent(in) :: cstring
character, pointer :: fstring(:)
integer :: slen, i
do i = 1, n
call c_f_pointer(cstring(i), fstring, [4])
write(*,*) fstring
end do
end subroutine print_cstring_array
# include "stdio.h"
void print_cstring_array(int* n, char* cstring[]);
void main(void) {
char* cstring[] = { "abc", "def", "ghi", "jkl" };
int n = 4;
print_cstring_array(&n, cstring);
}
答案 1 :(得分:1)
有时候解决方案比预期的要容易。原来,c_f_pointer(cptr, fptr[, shape])
采用数组形状作为可选参数来转换C指针数组(我在参考文献中错过了它):
subroutine print_cstring_array(n, cstring) bind(C)
use iso_c_binding, only: c_ptr, c_int, c_f_pointer, c_loc, c_null_char
implicit none
integer(kind=c_int), intent(in) :: n
type(c_ptr), target, intent(in) :: cstring
character(kind=c_char), dimension(:,:), pointer :: fptr
character(len=3), dimension(n) :: fstring
call c_f_pointer(c_loc(cstring), fptr, [3, n])
do i = 1, n
slen = 0
do while(fptr(slen+1,i) /= c_null_char)
slen = slen + 1
end do
fstring(i) = transfer(fptr(1:slen,i), fstring(i))
write(*,*) trim(fstring(i))
end do
end subroutine print_cstring_array
感谢@alk指向How to pass arrays of strings from both C and Fortran to Fortran?,因为我意识到shape
的可选c_f_pointer(cptr, fptr[, shape])
参数。