从fortran传递到c的字符串上的额外内容是什么?如何停止它?
我试图将一个字符串和一个值从fortran传递到c,它给了我奇怪的结果,我无法理解发生了什么从fortran传递到c的字符串上的额外内容是什么?如何停止它?,c,fortran,C,Fortran,我试图将一个字符串和一个值从fortran传递到c,它给了我奇怪的结果,我无法理解发生了什么 fortran integer rmat rmat = 4 call cprint("rmat ", rmat) END cprint_.c #include <stdio.h> void cprint_( char *string, int *wtf, int *var){ printf("string: %s
fortran
integer rmat
rmat = 4
call cprint("rmat ", rmat)
END
cprint_.c
#include <stdio.h>
void cprint_( char *string, int *wtf, int *var){
printf("string: %s wtf: %s var: %d\n", string, wtf, var);
}
fortran
整数rmat
rmat=4
呼叫cprint(“rmat”,rmat)
结束
cprint_uuc
#包括
void cprint_(字符*字符串,int*wtf,int*var){
printf(“字符串:%s wtf:%s变量:%d\n”,字符串,wtf,变量);
}
输出:
string:rmat convertedtest.f wtf:var:5您正在尝试使用
%s
格式设置程序打印int*
,该格式设置程序保留字符串您必须使用
%d
,并且不要忘记指针中的wtf
,因此您必须使用*wtf
取消对它的引用Fortran字符串相当于固定长度字符数组。语义上,它们不是以null结尾的。相反,由实现来跟踪它们的长度。历史上,实现通过各种不同的方法通过函数调用跟踪字符串长度,但最常见的方法可能是将长度作为一个附加的隐式参数传递。那肯定是你看到的
在某些实现中,额外的参数紧跟在字符串后面,而在其他实现中,所有字符串长度都位于参数列表的末尾。但是,与直接对应于对象的参数不同,长度参数通常是整数,而不是指针
您的测试虽然在技术上存在错误,但强烈表明字符串长度作为第三个参数传递(实际上是作为int
,而不是int*
)。因此,在适当的cprint\uu
函数处的第一次切割可能如下所示:
void cprint_(char *string, int *integer, int string_len) {
printf("string: '%.*s'\ninteger: %d\n", string, string_len, *integer);
}
如果要打印从Fortran收到的字符串,而不进行修改,那么可以指定适当的精度,如图所示,因为不能假定字符串已终止。但是,实际上,您可能希望打印的字符串的字符数少于其实际长度,因为数组的大小和其中有意义的字符数之间没有明确的区别。当分配给Fortran字符串的值小于其大小时,Fortran字符串用空格(“”
)右填充,Fortran程序通常假定实际数据仅扩展到最后一个非空白字符
但是要小心:如果您通过插入C字符串终止符来修改字符串,那么您也在更改Fortran端的数据。如果你必须复制,但通常你可以避免这样做,就像上面的例子那样。由@John Bollinger提供的答案是正确的。Fortran对字符串的处理与C完全不同。这是我几天前编写的一个示例代码,它将字符数组从C传递到Fortran,然后Fortran将其小写并将结果返回给C
module String_mod
implicit none
contains
subroutine getLowerCase(StrVec,StrVecLowerCase,lenStrVec) bind(C, name="getLowerCase")
use, intrinsic :: iso_c_binding, only: c_char, c_null_char, c_size_t
use, intrinsic :: iso_fortran_env, only: IK => int32
integer(c_size_t), intent(in), value :: lenStrVec
character(len=1,kind=C_char), intent(in) :: StrVec(lenStrVec)
character(len=1,kind=C_char), intent(inout) :: StrVecLowerCase(lenStrVec)
integer(IK), parameter :: duc = ichar('A') - ichar('a')
character :: ch
integer(IK) :: i
write(*,"(*(g0))") "From Inside Fortran@getLowerCase(): StrVec = ", (StrVec(i),i=1,lenStrVec)
write(*,"(*(g0))") "From Inside Fortran@getLowerCase(): lenStrVec = ", lenStrVec
do i = 1, lenStrVec
ch = StrVec(i)
if (ch>='A' .and. ch<='Z') ch = char(ichar(ch)-duc)
StrVecLowerCase(i) = ch
end do
end subroutine getLowerCase
end module String_mod
最简单的Fortran-2003字符串互操作方法是将Fortran字符串声明为字符数组,并将数组的长度从C显式传递到Fortran,反之亦然,如上面的示例所示。请注意,所有分配都发生在C端。Fortran 2018提供了在Fortran和C之间传递字符串(包括可分配字符串)的更为方便的方法,其中一些方法要求对Fortran代码进行无更改,并通过
ISO_Fortran_binding.h
对C代码进行最少的操作。例如,请参见。wtf
是int*
,不应使用%s
打印。您将int*wtf
视为一个带有%s
的字符串。另外,var
是不正确的类型。我认为您需要类似于printf的东西(“字符串:%s wtf:%d var:%d\n”,字符串,*wtf,*var)代码>没有关于Fortran端的评论(我不知道),但调用cprint
时仅使用两个参数似乎有点可疑。由于您没有使用Fortran 2003的标准化互操作性功能,请说明您使用的编译器(包括版本)。例如,许多编译器在使用带有字符参数的C函数时会传递额外的长度argunet。
#include <stdio.h>
// Fortran function's prototype
extern void getLowerCase(char [], char [], size_t );
int main(void)
{
char StrVec[] = "HELLO FORTRAN! YOU ROCK!";
size_t lenStrVec = sizeof(StrVec) / sizeof(StrVec[0]);
char StrVecLowerCase[ lenStrVec ];
getLowerCase( StrVec
, StrVecLowerCase
, lenStrVec
);
printf("From Inside C: %s\n", StrVecLowerCase);
return 0;
}
$ ifort -c String_mod.f90
$ icl -c main.c
$ icl String_mod.obj main.obj -o a.exe
$ a.exe
From Inside Fortran@getLowerCase(): StrVec = HELLO FORTRAN! YOU ROCK!
From Inside Fortran@getLowerCase(): lenStrVec = 25
From Inside C: hello fortran! you rock!