FORTRAN C/C++混合编程
转自别处:https://blog.csdn.net/zhuxianjianqi/article/details/8069585
混合编程在软件编程中是经常遇到的问题,尤其是C/C++/FORTRAN的混合编程,本文主要说明以上三种语言混合编程中经常遇到的问题,同时,也说明了不同平台下混合编程应注意的问题。
混合语言编程要注意的问题主要体现在:函数调用和数据结构的存储。
1 Windows平台
函数:由于Fortran编程语言没有大小写之分,Windows平台下的混合语言编程要注意的主要是大小写的问题,但是混编的时候需要C/C++为大写,否则Fortran调用找不到。考虑到编译器的差异,可以用下面的方式进行跨平台编程的函数声明。( C/C++编译器使用Microsoft Visual C++ 6.0, Fortran编译器使用 Digital Visual Fortran 6.0)。
假设一个C的函数为 void cFunction(); 那么,只需要在它的头文件里面进行如下定义即可:
#ifdef __cplusplus
extern “C” void {
#endif
extern void __stdcall CFunction();
#define cFunction CFUNCTION
#ifdef __cplusplus
}
#endif
这样,在Fortran或者C++的程序里面就可以直接调用了。
假设是一个Fortran函数SUBROUTINE FFUNCTION(); 那么,在C++头文件里进行如下的定义就可以了:
#ifdef __cplusplus
extern “C” void {
#endif
extern void __stdcall ffunction();
#define ffunction FFUNCTION
#ifdef __cplusplus
}
#endif
这样,就可以在C++的程序里面直接调用。由于C编译器里面,没有定义__cplusplus这个环境变量,因此,C文件里面,也可以直接使用这个头文件。
如果是一个C++函数,如: void cPlusplusFunction();和c函数一样,进行下面的定义即可:
#ifdef __cplusplus
extern “C” void {
#endif
extern void __stdcall cPlusplusFunction ();
#define cPlusplusFunction CPLUSPLUSFUNCTION
#ifdef __cplusplus
}
#endif
经过上面的定义后,所有的函数便可以在三种语言中自由调用。
在三种语言的混合编程中,还要注意函数的参数:字符串的传递问题。
Windows平台上的Fortran和C/C++的混合语言编程里,字符串的处理需要特别注意。Fortran的一个字符变量是定长的字符串,没有特别的终止符号,这不像C/C++。关于怎样表示字符、怎样存储它们的长度没有固定的约定。有些编译器把一个字符参数作为一对参数传送给一个程序,其中之一是保存这个串的地址,另一个是保存串的长度。Fortran里面字符串的结束就是靠字符串的长度确定的。
对含有字符串的函数,可以这样处理:
例如函数 void cCharFunction( char *msg );需要定义成:void cCharFunction( char *msg , int len ); 经过上面的define之后,在Fortran中,只需调用CCHARFUNCTION( MSG )即可。由于Fortran程序没有明显得字符串结束标志,这样,如果两个字符串连在一起的话,C的程序里就会取到这个连在一起的字符串,因此,最好在C的程序里面,对这个由Fortran程序得到的字符串进行处理,因为,从len这个变量,可以得到字符串长度,截取msg的前len个字符作为这个字符串的应有长度。
而如果是在Fortran程序里面,如函数:SUBROUTINE FCHARFUNCTION(FCHAR);经过相应的声明,进行下面的定义即可:
#define fCharFunction( fchar ), FCHARFUNCTION( fchar, strlen(fchar) )
这样,在C/C++程序里即可直接调用。
在这三种语言的混合编程里,还有一个小问题就是指针的问题。Fortran里面所有的变量都相当于C/C++里面的指针,所以,在C/C++里面的程序里,函数的参数应一律声明成指针的形式(除了字符串参数后面的长度)。
数据:混合编程里,数据上存在的差异也必须引起足够的重视。这体现在两个方面,数组和结构。
数组:Fortran语言里面,数组和C/C++里面的数组有些不同,这表现在两个方面,一是行列顺序,二是数组起始值,因此C++和Fortran混编使用数组的时候,尽量用一维数组,可以避免调整,如果是二维或三维,需要重新调整顺序,比较麻烦。
Fortran语言不同于C/C++的行优先,而使用列优先的方式。假设一个A数组,m行n列,那么采用行优先时的数据存放格式为:
a11,a12,…,a1n,a21,a22,…,a2n,……,am1,am2,…,amn
而采用列优先的数据存放格式为:
a11,a21,…,am1,a12,a22,…,am2,……,a1n,a2n,…,amn
行优先顺序推广到多维数组,规定为先排最右的下标;列优先顺序推广到多维数组,规定为先排最左的下标。这样,在混合语言编程里调用数据时,必须注意行列优先的差别,进行准确的调用。
数组的另一个差别是起始下标的不同。Fortran里面,默认的数组下标是以1开始的,而C/C++里面是从0开始的,所以,在调用里面要注意加一或者减一,以保证调用到正确的数据。
结构:在Fortran语言里的结构经过声明后,就被分配了空间,在C/C++里面也要声明它,
采用下面的方式:
Fortran:
COMMON /COLOR7/ C_RED, C_GREEN, C_BLUE
COMMON /NDDAT/ NID(NASIZE),XN(3,NASIZE)
混合语言编程要注意的问题主要体现在:函数调用和数据结构的存储。
1 Windows平台
函数:由于Fortran编程语言没有大小写之分,Windows平台下的混合语言编程要注意的主要是大小写的问题,但是混编的时候需要C/C++为大写,否则Fortran调用找不到。考虑到编译器的差异,可以用下面的方式进行跨平台编程的函数声明。( C/C++编译器使用Microsoft Visual C++ 6.0, Fortran编译器使用 Digital Visual Fortran 6.0)。
假设一个C的函数为 void cFunction(); 那么,只需要在它的头文件里面进行如下定义即可:
#ifdef __cplusplus
extern “C” void {
#endif
extern void __stdcall CFunction();
#define cFunction CFUNCTION
#ifdef __cplusplus
}
#endif
这样,在Fortran或者C++的程序里面就可以直接调用了。
假设是一个Fortran函数SUBROUTINE FFUNCTION(); 那么,在C++头文件里进行如下的定义就可以了:
#ifdef __cplusplus
extern “C” void {
#endif
extern void __stdcall ffunction();
#define ffunction FFUNCTION
#ifdef __cplusplus
}
#endif
这样,就可以在C++的程序里面直接调用。由于C编译器里面,没有定义__cplusplus这个环境变量,因此,C文件里面,也可以直接使用这个头文件。
如果是一个C++函数,如: void cPlusplusFunction();和c函数一样,进行下面的定义即可:
#ifdef __cplusplus
extern “C” void {
#endif
extern void __stdcall cPlusplusFunction ();
#define cPlusplusFunction CPLUSPLUSFUNCTION
#ifdef __cplusplus
}
#endif
经过上面的定义后,所有的函数便可以在三种语言中自由调用。
在三种语言的混合编程中,还要注意函数的参数:字符串的传递问题。
Windows平台上的Fortran和C/C++的混合语言编程里,字符串的处理需要特别注意。Fortran的一个字符变量是定长的字符串,没有特别的终止符号,这不像C/C++。关于怎样表示字符、怎样存储它们的长度没有固定的约定。有些编译器把一个字符参数作为一对参数传送给一个程序,其中之一是保存这个串的地址,另一个是保存串的长度。Fortran里面字符串的结束就是靠字符串的长度确定的。
对含有字符串的函数,可以这样处理:
例如函数 void cCharFunction( char *msg );需要定义成:void cCharFunction( char *msg , int len ); 经过上面的define之后,在Fortran中,只需调用CCHARFUNCTION( MSG )即可。由于Fortran程序没有明显得字符串结束标志,这样,如果两个字符串连在一起的话,C的程序里就会取到这个连在一起的字符串,因此,最好在C的程序里面,对这个由Fortran程序得到的字符串进行处理,因为,从len这个变量,可以得到字符串长度,截取msg的前len个字符作为这个字符串的应有长度。
而如果是在Fortran程序里面,如函数:SUBROUTINE FCHARFUNCTION(FCHAR);经过相应的声明,进行下面的定义即可:
#define fCharFunction( fchar ), FCHARFUNCTION( fchar, strlen(fchar) )
这样,在C/C++程序里即可直接调用。
在这三种语言的混合编程里,还有一个小问题就是指针的问题。Fortran里面所有的变量都相当于C/C++里面的指针,所以,在C/C++里面的程序里,函数的参数应一律声明成指针的形式(除了字符串参数后面的长度)。
数据:混合编程里,数据上存在的差异也必须引起足够的重视。这体现在两个方面,数组和结构。
数组:Fortran语言里面,数组和C/C++里面的数组有些不同,这表现在两个方面,一是行列顺序,二是数组起始值,因此C++和Fortran混编使用数组的时候,尽量用一维数组,可以避免调整,如果是二维或三维,需要重新调整顺序,比较麻烦。
Fortran语言不同于C/C++的行优先,而使用列优先的方式。假设一个A数组,m行n列,那么采用行优先时的数据存放格式为:
a11,a12,…,a1n,a21,a22,…,a2n,……,am1,am2,…,amn
而采用列优先的数据存放格式为:
a11,a21,…,am1,a12,a22,…,am2,……,a1n,a2n,…,amn
行优先顺序推广到多维数组,规定为先排最右的下标;列优先顺序推广到多维数组,规定为先排最左的下标。这样,在混合语言编程里调用数据时,必须注意行列优先的差别,进行准确的调用。
数组的另一个差别是起始下标的不同。Fortran里面,默认的数组下标是以1开始的,而C/C++里面是从0开始的,所以,在调用里面要注意加一或者减一,以保证调用到正确的数据。
结构:在Fortran语言里的结构经过声明后,就被分配了空间,在C/C++里面也要声明它,
采用下面的方式:
Fortran:
COMMON /COLOR7/ C_RED, C_GREEN, C_BLUE
COMMON /NDDAT/ NID(NASIZE),XN(3,NASIZE)
C/C++:
#ifdef __cplusplus
extern "C" {
#endif
#define color7 COLOR7
#define nddat NDDAT
extern struct {float c_red; float c_green; float c_blue;} color7;
extern struct {int nid[NASIZE]; float xn[NASIZE][3];} nddat;
#ifdef __cplusplus
}
#endif
2 Linux平台
Linux平台的混合语言编程和Windows平台上的基本没有什么区别,主要是在define上的不同。考虑到编译器的差异,在函数声明上,可以用下面的方式进行跨平台编程的函数声明。( C/C++编译器使用GNU gcc,Fortran编译器使用 pgi Fortran )。
假设一个C的函数为 void cFunction(); 那么,只需要在它的头文件里面进行定义即可:
#ifdef __cplusplus
extern “C” void {
#endif
extern void CFunction();
#define cFunction cfunction_
#ifdef __cplusplus
}
#endif
这样,在Fortran或者C++的程序里面就可以直接调用了。
注意:函数名应不大于31个字符。(即cfuntion_字符长度不大于32字符。PGI&Linux)
同样,对于C++和Fortran里面的函数,声明的时候,也只要改成小写,加下划线即可。
#ifdef __cplusplus
extern "C" {
#endif
#define color7 COLOR7
#define nddat NDDAT
extern struct {float c_red; float c_green; float c_blue;} color7;
extern struct {int nid[NASIZE]; float xn[NASIZE][3];} nddat;
#ifdef __cplusplus
}
#endif
2 Linux平台
Linux平台的混合语言编程和Windows平台上的基本没有什么区别,主要是在define上的不同。考虑到编译器的差异,在函数声明上,可以用下面的方式进行跨平台编程的函数声明。( C/C++编译器使用GNU gcc,Fortran编译器使用 pgi Fortran )。
假设一个C的函数为 void cFunction(); 那么,只需要在它的头文件里面进行定义即可:
#ifdef __cplusplus
extern “C” void {
#endif
extern void CFunction();
#define cFunction cfunction_
#ifdef __cplusplus
}
#endif
这样,在Fortran或者C++的程序里面就可以直接调用了。
注意:函数名应不大于31个字符。(即cfuntion_字符长度不大于32字符。PGI&Linux)
同样,对于C++和Fortran里面的函数,声明的时候,也只要改成小写,加下划线即可。
对于数组来说,变化和Windows是一致的。都是行列优先顺序不同的。而对于字符串来说,则不需要额外的注意,gcc编译器会处理好这个问题,也就是并不需要作出额外的改变。
数据结构的定义,也要改成小写加下划线的方式,如:
Fortran:
COMMON /COLOR7/ C_RED, C_GREEN, C_BLUE
COMMON /NDDAT/ NID(NASIZE),XN(3,NASIZE)
Fortran:
COMMON /COLOR7/ C_RED, C_GREEN, C_BLUE
COMMON /NDDAT/ NID(NASIZE),XN(3,NASIZE)
C/C++:
#ifdef __cplusplus
extern "C" {
#endif
#define color7 color7_
#define nddat nddat_
extern struct {float c_red; float c_green; float c_blue;} color7;
extern struct {int nid[NASIZE]; float xn[NASIZE][3];} nddat;
#ifdef __cplusplus
}
#endif
3 其它平台
对于Solaris平台,基本上和Linux平台完全一致,但是,考虑到Solaris大多运行在Sparc CPU上,它是采用big endian的,而基本的Windows和Linux运行在Intel或者AMD的X86平台,都是采用little endian的,这一点需要特别注意,这在读写数据文件时,应该给予足够的重视。其它的UNIX平台如HP UNIX,ULTRIX,IRIS等,一般都只有define上的微小差别,在字符串处理、结构及数组方面基本与Linux相同,对它们来说,考虑更多的应该是中央处理器的不同带来的差别。(如对齐、大端和小端)。
WIN32 平台define a A
ULTRIX || SPARC || IRIS || LINUX 平台 define a a_
HPUX || AIX 平台 勿须define
4 C/C++/FORTRAN 混合编程中的字符串处理
混编中经常会出现需要传递字符串的情况,而字符串的传递是一个较为麻烦的事情,在Fortran里面,字符串是没有结束符的,但是有长度的概念,也就是,编译器里面会给每一个字符串一个长度,以控制字符串的长度。但是这个长度参数在不同的平台下,其位置也是不同的(有的直接跟在字符串后面,有的则跟在函数参数的最后面),对于常见的平台如Windows,Linux, Solaris, HP UNIX, IRIS, 可以用如下方法定义:
#ifdef __cplusplus
extern "C" {
#endif
#define color7 color7_
#define nddat nddat_
extern struct {float c_red; float c_green; float c_blue;} color7;
extern struct {int nid[NASIZE]; float xn[NASIZE][3];} nddat;
#ifdef __cplusplus
}
#endif
3 其它平台
对于Solaris平台,基本上和Linux平台完全一致,但是,考虑到Solaris大多运行在Sparc CPU上,它是采用big endian的,而基本的Windows和Linux运行在Intel或者AMD的X86平台,都是采用little endian的,这一点需要特别注意,这在读写数据文件时,应该给予足够的重视。其它的UNIX平台如HP UNIX,ULTRIX,IRIS等,一般都只有define上的微小差别,在字符串处理、结构及数组方面基本与Linux相同,对它们来说,考虑更多的应该是中央处理器的不同带来的差别。(如对齐、大端和小端)。
WIN32 平台define a A
ULTRIX || SPARC || IRIS || LINUX 平台 define a a_
HPUX || AIX 平台 勿须define
4 C/C++/FORTRAN 混合编程中的字符串处理
混编中经常会出现需要传递字符串的情况,而字符串的传递是一个较为麻烦的事情,在Fortran里面,字符串是没有结束符的,但是有长度的概念,也就是,编译器里面会给每一个字符串一个长度,以控制字符串的长度。但是这个长度参数在不同的平台下,其位置也是不同的(有的直接跟在字符串后面,有的则跟在函数参数的最后面),对于常见的平台如Windows,Linux, Solaris, HP UNIX, IRIS, 可以用如下方法定义:
例如 c函数
void messag( char *msg1, int *where1, char *msg2, int *where2 )
{
printf(“ ……%s should be %d, while %s should be %d\n”, msg1, *where1, msg2, where2);
}
void messag( char *msg1, int *where1, char *msg2, int *where2 )
{
printf(“ ……%s should be %d, while %s should be %d\n”, msg1, *where1, msg2, where2);
}
如果要在Fortran里面调用的话,需要以下define:
#if defined ULTRIX || SPARC || IRIS || LINUX || WIN32
#if defined ULTRIX || SPARC || IRIS || LINUX
extern void __stdcall messag(char*, int*, char*, int*, int, int)
#define messag( s1, i1, s2, i2 ) messag_( s1, i1, s2, i2, strlen(s1), strlen(s2) )
#else
extern void __stdcall messag(char*, int, int*, char*, int, int*)
#define messag( s1, i1, s2, i2 ) MESSAGE( s1, strlen(s1), i1, s2, strlen(s2), i2 )
#endif
#else
extern void __stdcall messag(char*, int*, char*, int*, int, int)
#define messag( s1, i1, s2, i2 ) messag( s1, i1, s2, i2, strlen(s1), strlen(s2) )
#endif
#if defined ULTRIX || SPARC || IRIS || LINUX || WIN32
#if defined ULTRIX || SPARC || IRIS || LINUX
extern void __stdcall messag(char*, int*, char*, int*, int, int)
#define messag( s1, i1, s2, i2 ) messag_( s1, i1, s2, i2, strlen(s1), strlen(s2) )
#else
extern void __stdcall messag(char*, int, int*, char*, int, int*)
#define messag( s1, i1, s2, i2 ) MESSAGE( s1, strlen(s1), i1, s2, strlen(s2), i2 )
#endif
#else
extern void __stdcall messag(char*, int*, char*, int*, int, int)
#define messag( s1, i1, s2, i2 ) messag( s1, i1, s2, i2, strlen(s1), strlen(s2) )
#endif
如果用在C++中,加上相应的
#ifdef __cplusplus
extern “C” {
#endif
#ifdef __cplusplus
extern “C” {
#endif
#ifdef __cplusplus
}
#endif
}
#endif
Fortran里面便可以直接调用,如:
CALL MESSAG(char1, i1, char2,i2)
CALL MESSAG(char1, i1, char2,i2)
同样,在Fortran里面写的字符串处理函数,使用以上的Define和extern后,也可以在c里面直接调用。
5 文件读写
文件的读写也是混编中一个非常重要的问题,通常的问题发生于不同平台下的混编,以及不同Fortran编译器编译。
在FORTRAN中,文件的写入是由write语句完成的,而每一个write语句可一次性写入多个数据,构成一个数据块。而每一个无格式数据块都由下面3部分组成如图1所示:(1)数据块的开始标志,记录所有数据所占的字节数;(2)组成该数据块的各数据内容。整型数和浮点数,均占4个字节,低字节在前,高字节在后。各数据之间不空格。(3)每个数据块的结束标志,也为该数据块的字节数,而不是以回车换行符作为结束标志。各记录之间也没有分隔符。
除此之外,由于编程语言的差异,不同的编译器存储的格式也存在差异,如Visual FORTRAN与Digital FORTRAN在存储数据块中还存在着差别。差别在于在一个write语句中,Visual Fortran存储数据块的开始与结束标志是用一个字节表示,而在Digital Fortran在是用一个整形数,即四个字节来表示。如图2即Visual Fortran一个数据块最多可以存储2^7(128个字节),如果一个write语句要求写入的数据量大于128字节时,则按|80|..DATA..|80|80|…DATA…| 80|循环存入。所以在读取时,可以把它转化为Digital FORTRAN的存储形式。
5 文件读写
文件的读写也是混编中一个非常重要的问题,通常的问题发生于不同平台下的混编,以及不同Fortran编译器编译。
在FORTRAN中,文件的写入是由write语句完成的,而每一个write语句可一次性写入多个数据,构成一个数据块。而每一个无格式数据块都由下面3部分组成如图1所示:(1)数据块的开始标志,记录所有数据所占的字节数;(2)组成该数据块的各数据内容。整型数和浮点数,均占4个字节,低字节在前,高字节在后。各数据之间不空格。(3)每个数据块的结束标志,也为该数据块的字节数,而不是以回车换行符作为结束标志。各记录之间也没有分隔符。
除此之外,由于编程语言的差异,不同的编译器存储的格式也存在差异,如Visual FORTRAN与Digital FORTRAN在存储数据块中还存在着差别。差别在于在一个write语句中,Visual Fortran存储数据块的开始与结束标志是用一个字节表示,而在Digital Fortran在是用一个整形数,即四个字节来表示。如图2即Visual Fortran一个数据块最多可以存储2^7(128个字节),如果一个write语句要求写入的数据量大于128字节时,则按|80|..DATA..|80|80|…DATA…| 80|循环存入。所以在读取时,可以把它转化为Digital FORTRAN的存储形式。
file main.c
---------------
#include <stdio.h>
int main()
{
a();
return 0;
}
----------------
file a.f
***************
subroutine a()
implicit none
integer*4 stat
print*, 'start '
open(unit=12,iostat=stat,STATUS='NEW',file='test.txt')
write(unit=12,fmt=100)
100 format('test')
end
***************
两个文件,main.c 和 a.f ,编译成可执行程序main
运行之后界面输出结果如下:
start
这个说明已经调用了a,但是文件test.txt里面却没有写入内容。
当我直接用
***************
program a
implicit none
integer*4 stat
print*, 'start '
open(unit=12,iostat=stat,STATUS='NEW',file='test.txt')
write(unit=12,fmt=100)
100 format('test')
end
***************
时,显示的结果时start并且test.txt里面时写入了'test' 这个内容的。
也就是用c调用fortran的subroutine时,不能向文件里面写东西。