偶總結的FORTRAN/C/C++混合編程,大家有興趣就看看吧
轉自:
發(fā)信人: jxbking (arcane), 信區: Fortran
標 題: 偶總結的FORTRAN/C/C++混合編程,大家有興趣就看看吧
發(fā)信站: 南京大學(xué)小百合站 (Fri Jun 4 13:21:50 2004)
C/C++/FORTRAN 混合編程
混合編程在軟件編程中是經(jīng)常遇到的問(wèn)題,尤其是C/C++/FORTRAN的混合編程,本文主要說(shuō)明以上三種語(yǔ)言混合編程中經(jīng)常遇到的問(wèn)題,同時(shí),也說(shuō)明了不同平臺下混合編程應注意的問(wèn)題。
混合語(yǔ)言編程要注意的問(wèn)題主要體現在:函數調用和數據結構的存儲。
1 Windows平臺
函數:由于Fortran編程語(yǔ)言沒(méi)有大小寫(xiě)之分,Windows平臺下的混合語(yǔ)言編程要注意的主要是大小寫(xiě)的問(wèn)題??紤]到編譯器的差異,可以用下面的方式進(jìn)行跨平臺編程的函數聲明。( C/C++編譯器使用Microsoft Visual C++ 6.0, Fortran編譯器使用 Digital Visual Fortran 6.0)。
假設一個(gè)C的函數為 void cFunction(); 那么,只需要在它的頭文件里面進(jìn)行如下定義即可:
#ifdef __cplusplus
extern “C” void {
#endif
extern void __stdcall CFunction();
#define cFunction CFUNCTION
#ifdef __cplusplus
}
#endif
這樣,在Fortran或者C++的程序里面就可以直接調用了。
假設是一個(gè)Fortran函數SUBROUTINE FFUNCTION(); 那么,在C++頭文件里進(jìn)行如下的定義就可以了:
#ifdef __cplusplus
extern “C” void {
#endif
extern void __stdcall ffunction();
#define ffunction FFUNCTION
#ifdef __cplusplus
}
#endif
這樣,就可以在C++的程序里面直接調用。由于C編譯器里面,沒(méi)有定義__cplusplus這個(gè)環(huán)境變量,因此,C文件里面,也可以直接使用這個(gè)頭文件。
如果是一個(gè)C++函數,如: void cPlusplusFunction();和c函數一樣,進(jìn)行下面的定義即可:
#ifdef __cplusplus
extern “C” void {
#endif
extern void __stdcall cPlusplusFunction ();
#define cPlusplusFunction CPLUSPLUSFUNCTION
#ifdef __cplusplus
}
#endif
經(jīng)過(guò)上面的定義后,所有的函數便可以在三種語(yǔ)言中自由調用。
在三種語(yǔ)言的混合編程中,還要注意函數的參數:字符串的傳遞問(wèn)題。
Windows平臺上的Fortran和C/C++的混合語(yǔ)言編程里,字符串的處理需要特別注意。Fortran的一個(gè)字符變量是定長(cháng)的字符串,沒(méi)有特別的終止符號,這不像C/C++。關(guān)于怎樣表示字符、怎樣存儲它們的長(cháng)度沒(méi)有固定的約定。有些編譯器把一個(gè)字符參數作為一對參數傳送給一個(gè)程序,其中之一是保存這個(gè)串的地址,另一個(gè)是保存串的長(cháng)度。Fortran里面字符串的結束就是靠字符串的長(cháng)度確定的。
對含有字符串的函數,可以這樣處理:
例如函數 void cCharFunction( char *msg );需要定義成:void cCharFunction( char *msg , int len ); 經(jīng)過(guò)上面的define之后,在Fortran中,只需調用CCHARFUNCTION( MSG )即可。由于Fortran程序沒(méi)有明顯得字符串結束標志,這樣,如果兩個(gè)字符串連在一起的話(huà),C的程序里就會(huì )取到這個(gè)連在一起的字符串,因此,最好在C的程序里面,對這個(gè)由Fortran程序得到的字符串進(jìn)行處理,因為,從len這個(gè)變量,可以得到字符串長(cháng)度,截取msg的前l(fā)en個(gè)字符作為這個(gè)字符串的應有長(cháng)度。
而如果是在Fortran程序里面,如函數:SUBROUTINE FCHARFUNCTION(FCHAR);經(jīng)過(guò)相應的聲明,進(jìn)行下面的定義即可:
#define fCharFunction( fchar ), FCHARFUNCTION( fchar, strlen(fchar) )
這樣,在C/C++程序里即可直接調用。
在這三種語(yǔ)言的混合編程里,還有一個(gè)小問(wèn)題就是指針的問(wèn)題。Fortran里面所有的變量都相當于C/C++里面的指針,所以,在C/C++里面的程序里,函數的參數應一律聲明成指針的形式(除了字符串參數后面的長(cháng)度)。
數據:混合編程里,數據上存在的差異也必須引起足夠的重視。這體現在兩個(gè)方面,數組和結構。
數組:Fortran語(yǔ)言里面,數組和C/C++里面的數組有些不同,這表現在兩個(gè)方面,一是行列順序,二是數組起始值。
Fortran語(yǔ)言不同于C/C++的行優(yōu)先,而使用列優(yōu)先的方式。假設一個(gè)A數組,m行n列,那么采用行優(yōu)先時(shí)的數據存放格式為:
a11,a12,…,a1n,a21,a22,…,a2n,……,am1,am2,…,amn
而采用列優(yōu)先的數據存放格式為:
a11,a21,…,am1,a12,a22,…,am2,……,a1n,a2n,…,amn
行優(yōu)先順序推廣到多維數組,規定為先排最右的下標;列優(yōu)先順序推廣到多維數組,規定為先排最左的下標。這樣,在混合語(yǔ)言編程里調用數據時(shí),必須注意行列優(yōu)先的差別,進(jìn)行準確的調用。
數組的另一個(gè)差別是起始下標的不同。Fortran里面,默認的數組下標是以1開(kāi)始的,而C/C++里面是從0開(kāi)始的,所以,在調用里面要注意加一或者減一,以保證調用到正確的數據。
結構:在Fortran語(yǔ)言里的結構經(jīng)過(guò)聲明后,就被分配了空間,在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平臺的混合語(yǔ)言編程和Windows平臺上的基本沒(méi)有什么區別,主要是在define上的不同??紤]到編譯器的差異,在函數聲明上,可以用下面的方式進(jìn)行跨平臺編程的函數聲明。( C/C++編譯器使用GNU gcc,Fortran編譯器使用 pgi Fortran )。
假設一個(gè)C的函數為 void cFunction(); 那么,只需要在它的頭文件里面進(jìn)行定義即可:
#ifdef __cplusplus
extern “C” void {
#endif
extern void CFunction();
#define cFunction cfunction_
#ifdef __cplusplus
}
#endif
這樣,在Fortran或者C++的程序里面就可以直接調用了。
注意:函數名應不大于31個(gè)字符。(即cfuntion_字符長(cháng)度不大于32字符。PGI&Linux)
同樣,對于C++和Fortran里面的函數,聲明的時(shí)候,也只要改成小寫(xiě),加下劃線(xiàn)即可。
對于數組來(lái)說(shuō),變化和Windows是一致的。都是行列優(yōu)先順序不同的。而對于字符串來(lái)說(shuō),則不需要額外的注意,gcc編譯器會(huì )處理好這個(gè)問(wèn)題,也就是并不需要作出額外的改變。
數據結構的定義,也要改成小寫(xiě)加下劃線(xiàn)的方式,如:
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的,這一點(diǎn)需要特別注意,這在讀寫(xiě)數據文件時(shí),應該給予足夠的重視。其它的UNIX平臺如HP UNIX,ULTRIX,IRIS等,一般都只有define上的微小差別,在字符串處理、結構及數組方面基本與Linux相同,對它們來(lái)說(shuō),考慮更多的應該是中央處理器的不同帶來(lái)的差別。(如對齊、大端和小端)。
WIN32 平臺define a A
ULTRIX || SPARC || IRIS || LINUX 平臺 define a a_
HPUX || AIX 平臺 勿須define
4 C/C++/FORTRAN 混合編程中的字符串處理
混編中經(jīng)常會(huì )出現需要傳遞字符串的情況,而字符串的傳遞是一個(gè)較為麻煩的事情,在Fortran里面,字符串是沒(méi)有結束符的,但是有長(cháng)度的概念,也就是,編譯器里面會(huì )給每一個(gè)字符串一個(gè)長(cháng)度,以控制字符串的長(cháng)度。但是這個(gè)長(cháng)度參數在不同的平臺下,其位置也是不同的(有的直接跟在字符串后面,有的則跟在函數參數的最后面),對于常見(jiàn)的平臺如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);
}
如果要在Fortran里面調用的話(huà),需要以下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 /* WIN32 Platform */
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 /* Other Platform */
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
/* your extern code */
#ifdef __cplusplus
}
#endif
Fortran里面便可以直接調用,如:
CALL MESSAG(char1, i1, char2,i2)
同樣,在Fortran里面寫(xiě)的字符串處理函數,使用以上的Define和extern后,也可以在c里面直接調用。
5 文件讀寫(xiě)
文件的讀寫(xiě)也是混編中一個(gè)非常重要的問(wèn)題,通常的問(wèn)題發(fā)生于不同平臺下的混編,以及不同Fortran編譯器編譯。
在FORTRAN中,文件的寫(xiě)入是由write語(yǔ)句完成的,而每一個(gè)write語(yǔ)句可一次性寫(xiě)入多個(gè)數據,構成一個(gè)數據塊。而每一個(gè)無(wú)格式數據塊都由下面3部分組成如圖1所示:(1)數據塊的開(kāi)始標志,記錄所有數據所占的字節數;(2)組成該數據塊的各數據內容。整型數和浮點(diǎn)數,均占4個(gè)字節,低字節在前,高字節在后。各數據之間不空格。(3)每個(gè)數據塊的結束標志,也為該數據塊的字節數,而不是以回車(chē)換行符作為結束標志。各記錄之間也沒(méi)有分隔符。
除此之外,由于編程語(yǔ)言的差異,不同的編譯器存儲的格式也存在差異,如Visual FORTRAN與Digital FORTRAN在存儲數據塊中還存在著(zhù)差別。差別在于在一個(gè)write語(yǔ)句中,Visual Fortran存儲數據塊的開(kāi)始與結束標志是用一個(gè)字節表示,而在Digital Fortran在是用一個(gè)整形數,即四個(gè)字節來(lái)表示。如圖2即Visual Fortran一個(gè)數據塊最多可以存儲2^7(128個(gè)字節),如果一個(gè)write語(yǔ)句要求寫(xiě)入的數據量大于128字節時(shí),則按|80|..DATA..|80|80|…DATA…| 80|循環(huán)存入。所以在讀取時(shí),可以把它轉化為Digital FORTRAN的存儲
形式。
Arcane
Mail:
jxbking@163.net2003-9-12( 2003-10/30 update ).