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_ptrc_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 并使用 strncpymalloc 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 中删除它。