Weitere Angebote
Mischen C und Fortran
Mischen von C und Fortran
Beim Mischen von C- und Fortran-Routinen in einem Programm sollte man folgende Punkte beachten:
1. Namensgebung der Routinen
2. Parameterübergabe
3. Felder
4. COMMON-Blöcke oder Globale Variable
5. Wrapper
1. Namensgebung der Routinen:
In der Sprache C wird der Name so übernommen, wie er im Source-Code angegeben wurde.
In Fortran werden im Default-Fall (case insensitiv) alle Buchstaben des Routinenamens in Kleinbuchstaben umgewandelt. Ausnahme: Wenn mit der Compiler-Option -U übersetzt wird (case sensitiv), werden die Buchstaben nicht verändert. Es ist wichtig zu wissen, dass der f77-Compiler immer noch ein "_" an den Routinenamen anfügt.
Daraus folgt:
Ruft man von einem C-Programm aus eine Fortran-Routine auf, so muss beim Aufruf im C-Code ein Underscore ("_") als Namensschluss stehen.
ivar1 = function_name_ (ivar2);
INTEGER FUNCTION FUNCTION_NAME (IVAR)
Ruft man von einem Fortran-Programm aus eine C-Routine auf, so muss diese bei der Definition mit einem Underscore aufhören.
CALL C_ROUTINE(VAR)
void c_routine_(double *var);
Manche System-Unterprogramme sind mit beiden Namen vorhanden, mit und ohne Underscore, und damit sieht es so aus, als könne man ein und dieselbe System-Routine aus beiden Sprachen aufrufen. Dies trifft jedoch nicht zu. Beispiel: Die System-Routine "system" gibt es in libc.so und als "system_" in libftn.so, mit jeweils gleicher Leistung.
2. Parameterübergabe:
Bei der Kopplung C/Fortran dürfen in Fortran nur die Namen von Variablen, Feldern oder Matrix-Elementen übergeben werden. Dies bewirkt, dass die Werte auf der C-Seite wie "call-by-address" übergeben werden.
In C werden die Variablen mit "call-by-value" und die Felder mit "call-by-address" übergeben. Dies muss bei der Definition der Übergabeparameter bei der Definition der C-Routinen berücksichtigt werden (Pointer).
Die kompatiblen Typen für Unterprogramme sind:
Fortran | C | Bemerkung |
integer*2 x | short int *x; |
|
integer x | int *x; oder: long int *x; | a) |
logical x | int *x; oder: long int *x; | a) |
real x | float *x; |
|
double precision x | double *x; | a),b) |
complex x | struct{float real,imag;}x; |
|
double complex x | struct{double real,imag;}x; |
|
character*6 x | char x[6]; | c) |
a) Sind auch als Rückgabewerte von Funktionen zulässig, dürfen dann aber nicht als Pointer in C definiert werden. Zum Beispiel:
integer function name() int name_();
b) Fortran-Funktion REAL, dennoch Definition in C mit double precision:
real funktion name() double name_();
c) char-Felder sind nicht voll kompatibel.
Außerdem muss in den Routinen beachtet werden, dass für C das letzte Zeichen mit einem 0-Oktett besetzt ist, auch wenn es in Fortran nicht benötigt wird.
Bsp.:
string(l:l) = char(0)
3. Felder:
Felder werden in Fortran spaltenweise und in C zeilenweise im Speicher abgelegt.
Beispiel:
Matrix: | a(1,1) | a(1,2) | a(1,3) |
| a(2,1) | a(2,2) | a(2,3) |
Die Werte liegen wie folgt im Speicher direkt hintereinander:
Fortran: | a(1,1) | a(2,1) | a(1,2) | a(2,2) | a(1,3) | a(2,3) |
C: | a(1,1) | a(1,2) | a(1,3) | a(2,1) | a(2,2) | a(2,3) |
Daraus folgt, dass 2- und mehrdimensionale Felder vor der Übergabe C<->Fortran umgeordnet werden müssen.
4. COMMON-Blöcke oder Globale Variable
COMMON-Blöcke in Fortran und globale Variablen in C werden in die gleichen Speicherbereiche des Rechners abgelegt. Der Fortran-Compiler fügt an den Namen des COMMON-Blockes noch ein Underscore "_" an. So können diese Speicherbereiche mit Namen von Routinen beider Sprachen angesprochen werden, nur sollte man auf die Namensgebung achten.
Beispiel:
Fortran C
... ...
subroutine x() /+-> struct S{int ig; float fg;}bl_;
integer ic / void y(){
integer ip / int ip;
real fc / float fp;
real fp / ...
common /bl/ ic,fc <-+ ...
... ...
ip=ic ip = l_.ig;
fp=fc fp = bl_.fg;
... ...
end }
5. Wrapper:
Wenn man von C-Routinen aus vorgegebene Fortran-Routinen aufrufen möchte, kann man mit der Sprache C alle Anpassungen vornehmen, zum Beispiel "_" bei der Namensgebung beim Aufruf, call-by-address usw..
Eine Anpassung mit Fortran ist jedoch nicht möglich, wenn von Fortran aus vorgegebene C-Routinen aufgerufen werden sollen. Dazu dienen die Programme
"mkf2c" und "as".
Vorgehen:
1. Man schreibt das Fortran-Programm und ruft die C-Routine darin auf wie eine Fortran-Routine. Anschließend übersetzt man nur mit der Option -c, um das Object-File zu erhalten, z.B.: "f.o" .
Beispiel: f77 -c f.f
Quelle in Datei f.f:
program t
character*8 a,b
C die C-Routine strncmp vergleicht Characterstrings miteinander
integer i,j
integer strncmp
external strncmp
a = '12345678'
b = '01234567'
i = 8
j = strncmp(a,b,i)
write(*,*) j
end
2. Die C-Routine liege als Object-File vor oder man schreibt eine C-Funktion und übersetzt sie mit -c, um ein Object-File, z.B.: "c.o", zu erhalten.
3. Um den C-Modul mit dem Fortran-Modul zu verbinden, stellt man das Verbindungsprogramm her, den Wrapper. Es ist ebenfalls ein Object-File, z.B.: "s.o" .
Diese drei Object-Files bindet man anschließend:
f77 -o prog f.o c.o s.o
oder, für obiges Beispiel, da die Routine "strncmp" in der C-Standard-Lib enthalten ist:
f77 -o prog f.o s.o
Erstellung des Verbindungsprogramms:
A) Man erstellt eine Beschreibungsdatei der C-Routine in einer Datei mit der Endung ".fc". Die Beschreibungsdatei enthält nur die C-Definition der gewünschten Routine, ohne Semikolon, dafür aber mit einem Paar geschweifter Klammern.
Zum Beispiel wird in die Datei "s.fc" folgendes geschrieben:
int strncmp(char *a, char *b, int n){}
B) Mit dem Kommando "mkf2c" stellt man aus der Beschreibungsdatei eine Assembler-Quelle her, wobei die Option -l für Routinenamen größer als 6 Zeichen wichtig ist. Das erste Argument ist der Name der Beschreibungsdatei, das zweite der Name der zu erstellenden Assembler-Quell-Datei.
Beispiel:
mkf2c -l s.fc s.s
C) Die Assemblerquelle übersetzt man mit den Optionen -KPIC. Beispiel:
as -KPIC -o s.o s.s
(Mit mehrdimensionalen Feldern wurde der Wrapper noch nicht getestet.)
Bei Fragen und Problemen wenden Sie sich bitte an Dr. Corr, Tel.: 3075
Einrichtung








