zoukankan      html  css  js  c++  java
  • 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)
    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里面的函数,声明的时候,也只要改成小写,加下划线即可。
        对于数组来说,变化和Windows是一致的。都是行列优先顺序不同的。而对于字符串来说,则不需要额外的注意,gcc编译器会处理好这个问题,也就是并不需要作出额外的改变。
        数据结构的定义,也要改成小写加下划线的方式,如:
    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, 可以用如下方法定义:
    例如 c函数
    void messag( char *msg1, int *where1, char *msg2, int *where2 )
    {
    printf(“ ……%s should be %d, while %s should be %d ”, 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
    如果用在C++中,加上相应的
    #ifdef __cplusplus
    extern “C” {
    #endif
    #ifdef __cplusplus
    }
    #endif
    Fortran里面便可以直接调用,如:
    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的存储形式。

    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时,不能向文件里面写东西。
  • 相关阅读:
    use paramiko to connect remote server and execute command
    protect golang source code
    adjust jedi vim to python2 and python3
    install vim plugin local file offline
    add swap file if you only have 1G RAM
    datatables hyperlink in td
    django rest framework custom json format
    【JAVA基础】网络编程
    【JAVA基础】多线程
    【JAVA基础】String类的概述和使用
  • 原文地址:https://www.cnblogs.com/ljwan1222/p/9546244.html
Copyright © 2011-2022 走看看