Dynamische Speicherplatzreservierung

Eindimensionale Arrays

Bei Anwendungen, die Arrays verwenden, steht man häufig vor dem Problem, daß die Größe (Anzahl der Elemente) von Arrays zum Zeitpunkt der Übersetzung des Programms noch nicht feststeht, sondern sich erst während der Ausführung durch Benutzereingabe oder auf Grund der Daten ergibt. Sowohl in F als auch in C gibt es Mechanismen, um zur Laufzeit von Programmen dynamisch Speicherplatz anzufordern (zu "allokieren") und wieder freizugeben. Am einfachsten ist dies bei eindimensionalen Objekten (Vektoren), wo auch die Ähnlichkeit der beiden Sprachen am größten ist.
Die Verwendung dynamisch reservierter Arrays zerfällt in drei Teilaufgaben:
In F werden dynamisch allokierbare eindimensionale (und analog mehrdimensionale) Arrays durch ein "leeres" dimension- und das allocatable-Attribut bei der Variablendeklaration gekennzeichnet
type ,dimension(:),allocatable::variable_list
Wollte man also z.B. drei eindimensionale Arrays x, y und z vom Typ real und mit noch unbekannter Anzahl von Elementen definieren, so könnte das mit der Deklaration
real,dimension(:),allocatable::x,y,z
erfolgen. Diese Variablen dürfen aber noch nicht verwendet werden, solange für sie nicht explizit Speicherplatz angefordert wurde.
Die eigentliche Reservierung von Speicherplatz erfolgt mit dem allocate-Befehl
allocate(array_1(dim_1)[, array_2(dim_2),...][,stat= status])
Dabei beschreiben dim_1, dim_2, ... die Indexbereiche der Arrays, und status ist eine Statusvariable vom Typ integer, deren Wert anzeigt, ob die Reservierung erfolgreich war (status=0) oder nicht (status ¹ 0). Die Speicherplatzreservierung für die Vektoren x, y und z von vorhin könnte also
allocate(x(100),y(0:20),z(-15:15))
lauten, wenn man auf den Statustest verzichtet.
Werden Arrays nicht mehr benötigt, so kann ihr Speicherplatz mit deallocate explizit freigegeben werden
deallocate(array_1[, array_1,...])
also z.B. 
deallocate(y,z)
Es spricht übrigens nichts dagegen, ein deallokiertes Objekt neuerlich (u.U. mit einer anderen Anzahl von Elementen) zu allokieren.
In C werden dynamisch reservierbare eindimensionale Arrays am übersichtlichsten behandelt, indem man sie zunächst als Pointer deklariert und dann "vergißt", daß es eigentlich Pointer sind. Ein Pointer ist eine Variable, deren Inhalt nicht direkt einen Wert, sondern dessen Speicheradresse darstellt, d.h. sie "zeigt" auf die Adresse, unter der dieser Wert im Speicher zu finden ist-in unserem Fall wird das die Adresse des ersten Array-Elements sein. In Deklarationen werden Pointer dadurch gekennzeichnet, daß man einen Stern ("*") vor ihren Namen setzt und ihnen den Typ der Werte gibt, auf die sie zeigen
type *array_1[,* array_1,...];
Sollen analog zum Fall von F drei float-Arrays x, y und z deklariert werden, dann kann das z.B. so aussehen
float *x,*y,*z;
Zur eigentlichen Speicherplatzreservierung dienen die Funktionen malloc
array=(type*)malloc(num_bytes);
oder calloc
array=(type*)calloc( num_elements,size_element);
wo num_bytes die absolute Größe des Arrays in Bytes ist, bzw.  num_elements die Anzahl der zu reservierenden Array-Elemente und size_element die Größe eines Array-Elements (in Bytes) angeben. Dadurch wird ein zusammenhängender Bereich im Speicher angefordert, der groß genug ist, alle Elemente von array aufzunehmen. Obwohl es sich im Prinzip um ganze Zahlen handelt, sind alle Argumente formal vom Typ size_t und müssen/können bei Bedarf explizit durch einen cast-Operator konvertiert werden. Bei Verwendung von calloc statt malloc wird zusätzlich noch der reservierte Speicherplatz mit Nullen initialisiert. Wenn die Speicherplatzreservierung erfolgreich war, ist der Rückgabewert von malloc/calloc ein Pointer vom Typ void auf die erste Speicheradresse des reservierten Bereichs. Diese Adresse wird der Pointervariablen array (die auf das erste Array-Elemnent zeigen soll) zugewiesen, nachdem sie mit Hilfe eines cast auf den Typ von array konvertiert wurde. Ist die Reservierung nicht erfolgreich, so wird statt einer gültigen Speicheradresse der Wert NULL zurückgegeben. Den Speicherplatzbedarf eines einzelnen Array-Elements (in Bytes) kann man mit der Funktion
sizeof(type)
abfragen. Die Übersetzung des obigen Fortran-Beispiels nach C könnte damit folgendermaßen aussehen
x=(float*)malloc(100*sizeof(float));
y=(float*)malloc(21*sizeof(float));
z=(float*)malloc(31*sizeof(float));
(ebenfalls ohne Erfolgstest).
Die Rückgabe des für array reservierten Speicherplatzes erfolgt in C mit dem Befehl
free(array);
also z.B. 
free(y);
free(z);
Um in Ausdrücken oder Zuweisungen auf die Werte individueller Array-Elemente zuzugreifen, könnte man sich des bei der Deklaration von Pointern erwähnten "*"-Operators bedienen: Ist nämlich p ein Pointer, so bekommt man mit *p den Wert, der an der Speicherstelle steht, deren Adresse die (Pointer)-Variable p enthält. Analog liefert *(p+1) den an der nächsten, *(p+2) den an der übernächsten Adresse gespeicherten Wert, usw. Man könnte daher die einzelnen Elemente von array mit *array, *(array+1), *(array+2) usw. ansprechen. Glücklicherweise gibt es dafür die äquivalente Notation array[0], array[1], array[2], ..., d.h. array[i] ist der i-te Wert hinter der Startadresse von array. Damit ist, abgesehen von der Deklaration und Allokation/Deallokation, die Verwendung dynamisch erzeugter Arrays weitgehend identisch mit der von statischen Arrays (mit zur Compilationszeit fixierter Größe). So können im obigen Beispiel x, y und z als Vektoren mit den Elementen x[0] bis x[99], y[0] bis y[20] und z[0] bis z[30] verwendet werden-ganz so, als ob man sie statisch mittels
 float x[100],y[21],z[31];
deklariert hätte.



File translated from TEX by TTH, version 3.80.
On 1 May 2008, 11:09.