简体   繁体   English

C to Fotran:在结构中声明的指向 char 数组的 C 指针的互操作性

[英]C to Fotran: Interoperability of a C pointer to a char array, declared in a struct

I am creating Fortran bindings to a C library.我正在创建到 C 库的 Fortran 绑定。 The C header defines a struct as follows: C 头文件定义了一个struct ,如下所示:

struct frame {
  char *foo[4];
  int  bar;
};

(in reality, the C struct is much more complicated, I just stripped it down to the essential part). (实际上,C 结构体要复杂得多,我只是将其分解为基本部分)。 Now, the C library also provides a function that gives values to the struct.现在,C 库还提供了一个为结构体赋值的函数。 For the purposes of this question, I wrote a simplified replacement of such a function:出于这个问题的目的,我写了一个对这样一个函数的简化替换:

void setFrame(struct frame *fr) {
  fr->bar=12;
  printf("bar in C: %d\n", fr->bar);
}

(I don't really care about the value of foo , but I do care about the value of bar , and I won't get it correctly on the Fortran side if foo isn't declared properly). (我并不真正关心foo的值,但我确实关心bar的值,如果foo未正确声明,我将无法在 Fortran 端正确获取它)。 Now, this is my Fortran implementation of the above:现在,这是我对上述内容的 Fortran 实现:

module binding
use iso_c_binding
implicit none
private

type, bind(c), public :: frame
  integer(kind=c_int), dimension(4) :: foo
  integer(kind=c_int) :: bar
end type Frame

public :: setFrame

interface
  subroutine setFrame_C(fr) bind(c, name="setFrame")
  import :: c_ptr
  type(c_ptr), value :: fr
  end subroutine setFrame_C
end interface

contains

subroutine setFrame(fr)
type(frame), intent(inout), target :: fr
call setFrame_C(c_loc(fr))
end subroutine setFrame

end module binding

and this is a test Fortran program:这是一个测试 Fortran 程序:

program test
use binding
implicit none
type(frame) :: fr
print "(a,i0)","bar in Fortran (before calling setFrame): ",fr%bar
call setFrame(fr)
print "(a,i0)","bar in Fortran (after calling setFrame): ",fr%bar
end program test

Running the above program gives:运行上面的程序给出:

bar in Fortran (before calling setFrame): -1076110840
bar in C: 12
bar in Fortran (after calling setFrame): 12

meaning the Fortran binding works.意味着 Fortran 绑定有效。 Note that the equivalent of char *foo[4] is an integer array of c_int .请注意, char *foo[4]的等价物是c_int的整数数组。 This is a bit surprising because, although there is not much of a difference between a C integer and a C char, I would expect C char to be a one-byte integer.这有点令人惊讶,因为虽然 C 整数和 C char 之间没有太大区别,但我希望 C char 是一个单字节整数。 Now, if I declare char *foo[4] in a more "natural" way:现在,如果我以更“自然”的方式声明char *foo[4]

character(kind=c_char), dimension(4) :: foo

it doesn't work (I don't get the correct value for fr.bar ).它不起作用(我没有得到fr.bar的正确值)。 The reason is that, at least in gfortran 11.2.0, c_int is actually equal to 4 while c_char is equal to 1. Even character(kind=c_int) works.原因是,至少在 gfortran 11.2.0 中, c_int实际上等于 4 而c_char等于 1。甚至character(kind=c_int)工作。 I fact, character(kind=4) also works, although the compiler (correctly) issues a warning in this case.事实上, character(kind=4)也可以工作,尽管在这种情况下编译器(正确地)发出警告。

So my question is... what is actually the correct, "formal" way to treat char *foo[4] on the Fortran side, portable to another compiler that probably treats C char in another way?所以我的问题是......在 Fortran 端处理char *foo[4]真正正确的“正式”方式是什么,可移植到另一个可能以另一种方式处理 C char 的编译器?

The problem is that char *foo[4];问题是char *foo[4]; is an array of four pointers to char, so your interoperable declaration in Fortran has to be是一个由四个指向 char 的指针组成的数组,因此您在 Fortran 中的可互操作声明必须是

type, bind(c), public :: frame
  type(c_ptr), dimension(4) :: foo
  integer(kind=c_int) :: bar
end type Frame

Your way only happens to work because you are probably testing on a 32bit platform where a pointer has the same width as an int.您的方法只是碰巧有效,因为您可能正在 32 位平台上进行测试,其中指针与 int 具有相同的宽度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM