Fortran: Fortran und C: Fortran 2003
<<< zur Fortran-Startseite | |
<< Fortran und C | F >> |
< Fortran 90/95 und C | Fortran 2003 und C > |
In Fortran 2003 ist es viel einfacher auf C zu zugreifen, als in Fortran 95. Es wurde im Fortran 2003-Standard ein intrinsisches Modul namens iso_c_binding
vorgesehen, das die zum Zugriff auf C-Programme nötigen Elemente enthält.
Ein einfaches Beispiel
BearbeitenBeispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran 2003-Code: bsp.f95
Fortran 2003 (oder neuer)-Code |
program bsp implicit none interface function addition( a, b ) bind( c[, name="c_func"] ) use, intrinsic :: iso_c_binding real( kind = c_float ), value :: a real( kind = c_float ), value :: b real( kind = c_float ) :: addition end function addition end interface write (*,*) addition( 2.5, 3.3 ) ! Ausgabe: 5.8 end program bsp |
C-Code: bsp.c
Programmcode |
float addition(float a, float b) { return (a + b); } |
Makefile:
Programmcode |
FC = g95 # oder gfortran, ... CC = gcc # oder icc, ... bsp: bsp_c.o bsp_f95.o $(FC) -o bsp bsp_c.o bsp_f95.o bsp_c.o: bsp.c $(CC) -c -o bsp_c.o bsp.c bsp_f95.o: bsp.f95 $(FC) -c -o bsp_f95.o bsp.f95 .PHONY: clean clean: rm *.o |
Was ist neu gegenüber Fortran 95?
|
... | bind-Attribut, stellt unter anderem die Interoperabilität mit C bezüglich Prozedurnamenskonventionen nach der Übersetzung sicher. Mittels des optionalen Arguments "name" kann die Funktion in Fortran umbenannt werden. "c_func" ist dabei der Name der Funktion in C. |
|
... | Einbindung des intrinsischen Moduls iso_c_binding
|
|
... | C-Datentyp float .
|
|
... | call by value |
Datentyp-Zuordnung
BearbeitenDas iso_c_binding
-Modul stellt benannte Konstanten zur Verfügung, die bei Fortran-Datentypen als kind-Wert zu verwenden sind, um den jeweiligen C-Datentyp zu charakterisieren. Weist eine solche Konstante einen negativen Wert auf, dann ist keine Entsprechung von Fortran-Datentyp zu C-Datentyp vorhanden.
Fortran-Datentyp | Benannte iso_c_binding -Konstante (kind-Wert)
|
C-Datentyp |
---|---|---|
integer | c_int | int |
c_short | short int | |
c_long | long int | |
c_long_long | long long int | |
c_signed_char | signed char, unsigned char | |
c_size_t | size_t | |
c_int8_t | int8_t | |
c_int16_t | int16_t | |
c_int32_t | int32_t | |
c_int64_t | int64_t | |
c_int_least8_t | int_least8_t | |
c_int_least16_t | int_least16_t | |
c_int_least32_t | int_least32_t | |
c_int_least64_t | int_least64_t | |
c_int_fast8_t | int_fast8_t | |
c_int_fast16_t | int_fast16_t | |
c_int_fast32_t | int_fast32_t | |
c_int_fast64_t | int_fast64_t | |
c_intmax_t | intmax_t | |
c_intptr_t | intptr_t | |
real | c_float | float |
c_double | double | |
c_long_double | long double | |
complex | c_float_complex | float _Complex |
c_double_complex | double _Complex | |
c_long_double_complex | long double _Complex | |
logical | c_bool | _Bool |
character | c_char | char |
Quelle: J3/04-007 Fortran 2003 Working Draft |
Das iso_c_binding
-Modul stellt keine speziellen kind-Werte für unsigned
-Integer-Datentypen zur Verfügung. Im Bedarfsfall sind die entsprechenden kind-Werte für die signed
-Datentypen zu verwendet.
Nicht jeder Fortran-Compiler unterstützt alle genannten C-Datentypen und die unterstützten Datentypen können sich compilerspezifisch in der Byteanzahl unterscheiden. Die nächste Tabelle zeigt kurz auf, welche kind-Konstanten derzeit (1. Dez. 2007) von einigen Compilern definiert werden.
c_int | c_short | c_long | c_long_long | c_signed_char | c_size_t | c_int8_t | |
---|---|---|---|---|---|---|---|
g95 | 4 | 2 | 4 | 8 | 1 | 4 | 1 |
gfortran | 4 | 2 | 4 | 8 | 1 | 4 | 1 |
ifort | 4 | 2 | 4 | 8 | 1 | 4 | 1 |
f95 | 4 | 2 | 4 | 8 | 1 | 4 | |
c_int16_t | c_int32_t | c_int64_t | c_int_least8_t | c_int_least16_t | c_int_least32_t | c_int_least64_t | |
g95 | 2 | 4 | 8 | 1 | 2 | 4 | 8 |
gfortran | 2 | 4 | 8 | 1 | 2 | 4 | 8 |
ifort | 2 | 4 | 8 | 1 | 2 | 4 | 8 |
f95 | 1 | 2 | 4 | 8 | |||
c_int_fast8_t | c_int_fast16_t | c_int_fast32_t | c_int_fast64_t | c_intmax_t | c_intptr_t | c_float | |
g95 | 1 | 4 | 4 | 8 | 4 | 4 | 4 |
gfortran | (1) | (1) | (1) | (1) | 8 | (1); 4 oder 8 | 4 |
ifort | 1 | 4 | 4 | 8 | 8 | 4 | 4 |
f95 | 1 | 2 | 4 | 8 | 8 | 4 | |
c_double | c_long_double | c_float_complex | c_double_complex | c_long_double_complex | c_bool | c_char | |
g95 | 8 | -1 | 4 | 8 | -1 | -1 | 1 |
gfortran | 8 | (1), oft 10 | 4 | 8 | (1), oft 10 | 1 | 1 |
ifort | 8 | -1 | 4 | 8 | -1 | 1 | 1 |
f95 | 8 | -3 | 4 | 8 | -3 | 1 | 1 |
(1) Der Wert ist systemabhängig
- g95: g95 0.91! Nov 29 2007
- gfortran: GNU Fortran (GCC) 4.3.0 20071201 (experimental)
- ifort: Intel Fortran Compiler 10.1 20070913
- f95: Sun Studio Express, June 2007
positive Zahl | Konstante ist bekannt, C-Datentyp wird unterstützt |
negative Zahl | Konstante ist bekannt, C-Datentyp wird nicht unterstützt |
unbekannte Konstante |
„call by value“ vs. „call by reference“
BearbeitenIm einführenden Beispiel wurden die Funktionsargumente „call by value“ übergeben. Das Variablenattribut value
stellt dieses Verhalten sicher. Wird dieses Attribut nicht gesetzt, so gilt „call by reference“.
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp implicit none interface subroutine zahl( a, b ) bind( c ) use, intrinsic :: iso_c_binding integer( kind=c_int ), value :: a integer( kind=c_int ) :: b end subroutine zahl end interface call zahl(5, 7) ! Ausgabe: ! Ergebnis = 35 end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> void zahl(int a, int *b) { printf("%s%d\n", "Ergebnis = ", a * *b); } |
Globale C-Variablen
BearbeitenMuss in Fortran auf globale C-Variablen zugegriffen werden, so sind diese im Gültigkeitsbereich eines Fortran-Modul zu spezifizieren.
Felder
BearbeitenInteroperabilität zwischen Fortran und C ist nur mit Feldern definierter Größe gegeben. Allozierbare Felder oder Zeigerfelder sind nicht erlaubt.
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran 2003 (oder neuer)-Code |
program bsp implicit none integer, dimension( 3 ) :: a = (/ 1, 2, 3 /) interface subroutine feld1( f ) bind( c ) use, intrinsic :: iso_c_binding integer( c_int ), dimension(*) :: f end subroutine feld1 end interface write (*,*) "Feld a vorher: ", a call feld1( a ) write (*,*) "Feld a nachher: ", a ! Ausgabe: ! Feld a vorher: 1 2 3 ! Feld a nachher: 999 2 3 end program bsp |
Programmcode |
void feld1(int f[]) { f[0] = 999; } |
Übergabe von Zeichenketten
BearbeitenBeispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): nein
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
- gfortran lehnt dies ab, da dies ungültiger Fortran-Syntax ist: Fehler: Character argument 'str_in' at (1) must be length 1 because procedure 'string1' is BIND(C)
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp use, intrinsic :: iso_c_binding implicit none interface subroutine string1( str_in ) bind( c ) use, intrinsic :: iso_c_binding character( kind=c_char, len=* ) :: str_in ! Ungültiges Fortran 2003 da nur len=1 erlaubt ist end subroutine string1 end interface call string1( c_char_"Greetings from Fortran" // c_null_char ) ! Ausgabe: ! "Greetings from Fortran" end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> void string1(char str_in[]) { printf("%s \n", str_in); } |
Für len=1 funktioniert das Beispiel mit allen angeführten Compilern. Das len
-Attribut kann im Übrigen auch weggelassen werden; einige unterstützen als compilerspezifische Erweitungen auch andere Längen. Im Fortran 2003-Working-Draft wird in „Note 15.23“ eine andere Möglichkeit für die Übergabe von Zeichenketten angeführt. Diese folgt im wesentlichen der Annahme:
- C-Zeichenketten sind
char
-Felder mit einem terminierenden\0
-Zeichen und können deshalb in Fortran als Felder vom Datentypcharacter
mit einem kind-Wertc_char
angesprochen werden.
Daher wird im Interface der Dummy-Parameter in Form eines Feldes aus Zeichen des Typs c_char
deklariert. Das entspräche auch genau der aufzurufenden C-Funktion. Allerdings ist solcherart verfasster Code dann nicht mit allen Compilern übersetzbar (Stand Juli 2007).
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: nein
Anmerkungen:
- Sun Studio Express-Fehlermeldung:
- ... Zusicherung >>addr<< nicht erf?llt.
- "bsp.f95", Line = 13, Column = 1: INTERNAL: Interrupt: Abgebrochen
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp use, intrinsic :: iso_c_binding implicit none interface subroutine string1( str_in ) bind( c ) use, intrinsic :: iso_c_binding character( kind=c_char ), dimension(*) :: str_in end subroutine string1 end interface call string1( c_char_"Greetings from Fortran" // c_null_char ) ! Ausgabe: ! "Greetings from Fortran" end program bsp |
Beispiel:
Damit das obige Beispiel auch mit dem "Sun Studio-Fortrancompiler" funktioniert, darf der String nicht direkt dem Unterprogramm übergeben werden, sondern muss zuvor in einer Variablen abgelegt werden.
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp use, intrinsic :: iso_c_binding implicit none character(len=35) :: str interface subroutine string1( str_in ) bind( c ) use, intrinsic :: iso_c_binding character( kind=c_char ), dimension(*) :: str_in end subroutine string1 end interface str = "Greetings from Fortran" // c_null_char call string1( str ) ! Ausgabe: ! "Greetings from Fortran" end program bsp |
Benannte iso_c_binding
-Konstanten für Zeichen mit spezieller Bedeutung in C:
Benannte Konstante | Wert |
---|---|
c_null_char | '\0' |
c_alert | '\a' |
c_backspace | '\b' |
c_form_feed | '\f' |
c_new_line | '\n' |
c_carriage_return | '\r' |
c_horizontal_tab | '\t' |
c_vertical_tab | '\v' |
Enumerationen
BearbeitenMit Fortran 2003 sind auch in dieser Programmiersprache Enumerationen (Aufzählungstypen) möglich. Die Werte in einer solchen Enumeration besitzen einen integer
-Datentyp. Der kind
-Wert ist nicht festgelegt, wird jedoch so gewählt, dass im Rahmen der jeweiligen Möglichkeiten alle Enumeratoren erfasst sind.
Die von C-Enumerationen bekannten Eigenschaften gelten gleichermaßen für Fortran-Enumerationen, z.B.:
- ohne explizite Zuweisung von Werten wird mit dem Wert 0 gestartet.
- ohne explizite Zuweisung von Werten wird in der Anordnungsreihenfolge der Elemente sukzessiv immer um 1 hochgezählt.
- Wurde dem Vorgängerelement eine Ganzzahl zugewiesen, dem Element jedoch nicht, so ist der Wert dieses Elementes die dem Vorgängerelement zugeordnete Ganzzahl + 1.
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: nein
- Sun Studio Express - June 2007: nein
Anmerkungen:
- Sun-Fortran-Compiler und Intel-Fortran-Compiler unterstützen momentan noch keine Enumerationen.
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp implicit none enum, bind(c) enumerator :: MO=1, DI=2, MI=3, DO=4, FR=5, SA=6, SO=7 end enum interface subroutine tag( w ) bind( c ) use, intrinsic :: iso_c_binding integer( c_int ), value :: w end subroutine tag end interface call tag(MI); ! Ausgabe: ! Mittwoch end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> typedef enum { MO=1, DI=2, MI=3, DO=4, FR=5, SA=6, SO=7 } wochentag; void tag(wochentag w) { switch(w) { case MO: printf("Montag\n"); break; case DI: printf("Dienstag\n"); break; case MI: printf("Mittwoch\n"); break; case DO: printf("Donnerstag\n"); break; case FR: printf("Freitag\n"); break; case SA: printf("Samstag\n"); break; case SO: printf("Sonntag\n"); break; default: printf("Kein Tag\n"); } } |
Zeiger
BearbeitenFür das C-Zeiger-Handling stellt Fortran 2003 im iso_c_binding
-Modul den Datenverbund c_ptr
und einige Unterprogramme bereit.
UP | Beschreibung |
---|---|
l = c_associated ( c_ptr1 [, c_ptr2] ) | Prüft den Assoziationsstatus von c_ptr1. Diese Funktion ermittelt also, ob c_ptr1 überhaupt assoziert ist, oder ob c_ptr1 mit c_ptr2 assoziert ist. |
c_ptr = c_loc ( x ) | Gibt die Adresse von x zurück. |
c_f_pointer ( c_ptr, fptr [, shape] ) | Wandelt einen C-Zeiger c_ptr in einen Fortran-Zeiger fptr um. Die Vorgabe von shape ist nur dann erforderlich und möglich, wenn fptr ein Feld ist. fptr ist intent( inout ) .
|
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
- Sun-Linker-Warnmeldung:
- Warning: alignment 4 of symbol `ptr1', ... in bsp_c.o is smaller than 16 in bsp_f90.o
- Intel-Linker-Warnmeldung:
- Warning: alignment 4 of symbol `ptr1', ... in bsp_c.o is smaller than 8 in bsp_f90.o
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
module cglob use, intrinsic :: iso_c_binding type( c_ptr ), bind( c ) :: ptr1, ptr2, ptr3 real( kind=c_float ), target, bind( c ) :: a, b end module cglob program bsp use cglob implicit none real, pointer :: p => null() ! *** Zuordnungsstatus *** write (*,*) "Ist ptr1 assoziert? ", c_associated( ptr1 ) write (*,*) "Ist ptr2 assoziert? ", c_associated( ptr2 ) write (*,*) "Ist ptr2 mit ptr3 assoziert? ", c_associated( ptr2, ptr3 ) write (*,*) "Ist ptr2 mit &a assoziert? ", c_associated( ptr2, c_loc(a) ) ! *** Wert von ptr2? *** call c_f_pointer( ptr2, p ) write (*,*) "Wert von ptr2? ", p ! *** ptr1 neu setzen *** ptr1 = c_loc( b ) ! *** Wert von ptr1 *** call c_f_pointer( ptr1, p ) write (*,*) "Wert von ptr1? ", p ! Ausgabe: ! Ist ptr1 assoziert? F ! Ist ptr2 assoziert? T ! Ist ptr2 mit ptr3 assoziert? F ! Ist ptr2 mit &a assoziert? T ! Wert von ptr2 (bzw. p)? 5555.66 ! Wert von ptr1 (bzw. p)? -12.3 end program bsp |
C-Code bsp.c:
Programmcode |
float a = 5555.66; float b = -12.3; float *ptr1 = 0; float *ptr2 = &a; float *ptr3 = &b; |
Nun ist die Zeiger-Verwendung wie im obigen Beispiel skizziert, eher die Ausnahme als die Regel.
Wesentlich häufiger trifft man Zeiger in C-Bibliotheken im Zusammenhang mit Funktionen an. Dort dienen sie aus Anwendersicht als return
-Werte oder der Parameterübergabe „call-by-reference“. Oft sind dabei auch Zeiger auf Strukuren im Spiel. Näheres dazu folgt im nächsten Abschnitt.
Für C-Zeiger auf den Wert NULL
bietet das ISO-C-Binding-Modul die Konstante C_NULL_PTR
.
Datenverbund
BearbeitenDie einzelnen Datenelemente der Struktur / des Datenverbundes müssen in Fortran und C selbstverständlich äquivalenten Datentyp aufweisen. Die Bezeichnungen der jeweiligen Datenelemente müssen nicht beibehalten werden. Sehr wohl müssen aber die Positionen der Einzelelemente im Fortran-Datenverbund der nachzubildenden C-Struktur entsprechen.
Für die Gewährleistung der Interoperabilität darf ein an C gebundener Datenverbund in Fortran (struct
) keine Zeiger mit dem pointer
-Attribut oder allozierbaren Felder als Datenelemente enthalten. Sind in der C-Struktur Zeiger vorhanden, so sind diese im entsprechenden Fortran-Datenverbund mittels type( c_ptr )
zu beschreiben.
Für Bitfelder oder Unions gibt es in Fortran keine entsprechenden Gegenstücke.
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
- gfortran: Im Gegensatz zu den anderen Compilern müssen bei der Deklaration der Subroutine print_v im Interface die Klammern für die leere Parameterliste vor dem bind(C) zwingend angestschrieben werden, ansonsten Fehlermeldung beim Compilieren; diese Klammer ist im Fortran 2003 Standard vorgeschrieben.
Fortran 2003 (oder neuer)-Code |
module cglob use, intrinsic :: iso_c_binding type, bind( c ) :: verbund integer( kind=c_int ) :: a real( kind=c_float ) :: b end type verbund type(verbund), bind( c ) :: v end module cglob program bsp use cglob implicit none interface subroutine set_v( a_in, b_in) bind( c ) use, intrinsic :: iso_c_binding integer( c_int ), value :: a_in real( c_float ), value :: b_in end subroutine set_v subroutine print_v() bind( c ) end subroutine print_v end interface call set_v( 5, -10.9) call print_v() write (*,*) "Fortran-Ausgabe: ", v v%a = 99 call print_v() write (*,*) "Fortran-Ausgabe: ", v ! Ausgabe: ! C-Ausgabe: 5 -10.900000 ! Fortran-Ausgabe: 5 -10.9 ! C-Ausgabe: 99 -10.900000 ! Fortran-Ausgabe: 99 -10.9 end program bsp |
Programmcode |
#include <stdio.h> typedef struct { int a; float b; } verbund; verbund v; void set_v(int a_in, float b_in) { v.a = a_in; v.b = b_in; } void print_v() { printf("C-Ausgabe: %i %f\n", v.a, v.b); } |
Beispiel: Struktur als Rückgabewert und Argument einer Funktion - Teil 1
Bekannt seien zwei Funktionsprototypen in der Programmiersprache C:
Xyz *get_xyz();
und
void set_xyz(Xyz *x);
Xyz sei ein C-struct
, dessen Inhalt hier nicht näher interessiert.
Die Schnittstelle, mit dem in Fortran die C-Anbindung der Funktionen realisiert wird, könnte so aussehen:
interface function get_xyz() bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ) :: get_xyz end function get_xyz subroutine set_xyz( x ) bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ), value :: x end subroutine set_xyz end interface
Und schon können diese Funktionen auch in Fortranroutinen direkt Anwendung finden, z.B:
type( c_ptr ) :: x56 x56 = get_xyz() call set_xyz( x56 )
Wie man anhand dieses Beispiels erkennt, ist die interne Struktur von Xyz
in diesem Fall vollkommen irrelevant. Es ist nicht nötig, in der API-Dokumentation oder in den C-Headerdateien nachzuforschen, wie der C-struct
konkret aufgebaut ist. Für den Fortran-Programmierer ist dies immer ein type( c_ptr )
(natürlich nur, solange Zeiger auf Strukturen gefordert sind). Wird ein Zeiger des Typs c_ptr
nur via Fortranprogramm zwischen verschiedenen C-Funktionen übergeben oder zurückgeliefert, so ist auch keine Umwandlung in einen Fortran-Zeiger erforderlich und wäre mangels genauer Kenntnis des Aufbaus von Xyz
hier auch nicht möglich. Erst dann, wenn von Fortran aus auf einzelne Elemente eines C-struct
zugegriffen werden soll oder mit Kopien der Struktur hantiert wird, muss diese Struktur in Fortran nachgebaut werden.
Beispiel: Struktur als Rückgabewert und Argument einer Funktion - Teil 2
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran 2003 (oder neuer)-Code |
module test use, intrinsic :: iso_c_binding implicit none type, bind( c ) :: A ! Das funktioniert nicht mit allen Kompilern; versuchen Sie ansonsten: ! type A ! sequence integer( c_int ) :: xc, yc type( c_ptr ) :: str end type interface function get_a() bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ) :: get_a end function get_a subroutine print_a( x ) bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ), value :: x end subroutine print_a end interface end module test program main use test implicit none type( c_ptr ) :: x type( A ), pointer :: fptr character( len=9 ), pointer :: strptr x = get_a() ! C-Ausgabe call print_a( x ) ! Ausgabe: ! x = 5 ! y = 997 ! str = Irgendwas ! Fortran-Ausgabe call c_f_pointer( x, fptr) call c_f_pointer( fptr%str, strptr ) ! <--- Fehlerquelle bei gfortran write(*,*) fptr%xc, fptr%yc, strptr ! Ausgabe ! 5 997 Irgendwas end program main |
Programmcode |
#include <stdlib.h> #include <stdio.h> typedef struct { int x; int y; const char *str; } A; A *get_a() { A *a = ( A * )malloc( sizeof( A ) ); a->x = 5; a->y = 997; a->str = "Irgendwas"; return a; } void print_a( A *v ) { printf("x = %d\n", v->x); printf("y = %d\n", v->y); printf("str = %s\n", v->str); } |
Bei der Umwandlung des C-String-Zeigers in einen Fortran-Zeiger ist die genaue Stringlänge erforderlich. Ist diese zu klein gewählt, so ist nur ein Teil des C-Strings über den Fortran-Zeiger sichtbar. Wird diese Stringlänge zu groß gewählt, dann wird diese Länge auch voll ausgenutzt und nach dem eigentlich gewünschten String folgen noch ein Menge x-beliebige Zeichen, da in Fortran der C-String-Begrenzer \0
nicht die Bedeutung wie in C besitzt.
Im obigen Beispiel wurde die Speicherplatzreservierung und Wertebelegung für eine Variable des Typs A
in einer C-Funktion erledigt. Nun soll diese Aufgabe im Fortran-Programm wahrgenommen werden und dann diese in Fortran mit Werten belegte Variable an die print_a
-Funktion übergeben werden. Kein Problem, möchte man im ersten Augenblick meinen. Doch der c_ptr
-Typ im Datenverbund für den Zeiger auf eine Zeichenkette macht Probleme. Der g95-Compiler duldet bspw. in der c_loc
-Funktion keine Zeichenketten mit einer Länge größer als 1. Der Compiler "castet" auch nicht von selbst im Datenverbundkonstruktor eine Zeichenkette in den Typ c_ptr
. Eine Möglichkeit, diese Probleme zu umgehen, besteht darin, einfach einer C-Funktion einen String zu übergeben und die Adresse dieses Strings zurückgeben zu lassen. Diese Variante wird auch im folgenden Beispiel verwendet.
Beispiel: Struktur als Rückgabewert und Argument einer Funktion - Teil 3
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran 2003 (oder neuer)-Code |
module test use, intrinsic :: iso_c_binding implicit none type, bind( C ) :: A integer( c_int ) :: xc, yc type( c_ptr ) :: str end type type( A ), target :: a1 ! bei Verwendung des g95 könnte diese Var.deklaration ! auch im Hauptprogramm als lokale Variable ! vorgenommen werden. Der Sun-Compiler erlaubt ! kein gleichzeitiges C-Binding mit einer lokalen Var. interface subroutine print_a( x ) bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ), value :: x end subroutine print_a function c_string_addr( s ) bind( c ) use, intrinsic :: iso_c_binding character( c_char ) :: s type( c_ptr ) :: c_string_addr end function c_string_addr end interface end module test program main use test implicit none character( len = 30 ) :: str str = "Das ist ein Beispiel" // C_NULL_CHAR a1 = A( 14, 56, c_string_addr( str ) ) call print_a( c_loc( a1 ) ) ! Ausgabe: ! x = 14 ! y = 56 ! str = Das ist ein Beispiel end program main |
Programmcode |
#include <stdlib.h> #include <stdio.h> typedef struct { int x; int y; const char *str; } A; void print_a( A *v ) { printf("x = %d\n", v->x); printf("y = %d\n", v->y); printf("str = %s\n", v->str); } char *c_string_addr( char *str ) { return str; } |
Problematische Kamelhöckerschreibweise?
BearbeitenC ist case-sensitive, Fortran nicht. Oft sind Funktionen in C-Bibliotheken in der Kamelhöckerschreibweise vorzufinden, z.B.
void writeHallo();
Die Einbindung dieser C-Funktion in ein Fortran-Programm mit
interface subroutine writeHallo() bind(c) end subroutine writeHallo end interface
könnte beim Linken eine Fehlermeldung der Art
undefined reference to `writehallo'
liefern.
Was ist passiert? Wie schon angedeutet, ist Fortran case-insensitive. Für einen Fortran-Compiler ist writeHallo
gleich writehallo
oder WRITEHaLLO
. Für einen C-Compiler wären das alles unterschiedliche Funktionsbezeichner. Also wandelt der Fortran-Compiler die "unnütze" Groß-/Kleinschreibung in eine einheitliche Schreibweise um und der C-Compiler nicht. Dementsprechend findet der Linker auch keine Funktion writehallo
. Es gibt eben nur die nicht-idente C-Funktion writeHallo
.
Aber auch für dieses Problem gibt es in Fortran eine Lösung. Mit dem bind
-Attribut können zusätzlich auch noch Labels (Benennungen) vergeben werden, z.B.:
bind( c, name="xyz" )
Solche Labels in Stringform sind nicht von der "Kopf ab"-Strategie des Fortran-Compilers betroffen. Das ist keine C-Binding-Spezialität, sondern trifft generell für alle Zeichenketten zu. Ein
interface subroutine writeHallo() bind(c, name="writeHallo") end subroutine writeHallo end interface
sollte das beschriebene Link-Problem lösen.
Sonstiges
Bearbeiten- Auch C-Funktionszeiger kennt das
iso_c_binding
-Modul. Zu diesem Zwecke gibt es den Datenverbundc_funptr
, einige Umwandlungsfunktionen ähnlich jenen im Abschnitt Zeiger behandelten und die KonstanteC_NULL_FUNPTR
.
<<< zur Fortran-Startseite | |
<< Fortran und C | F >> |
< Fortran 90/95 und C | Fortran 2003 und C > |