Funktionen für Felder und Zeichenketten

malloc() und calloc() – Dynamische Speicherreservierung

Soll die Größe eines Feldes erst zur Laufzeit bestimmt werden, so ermöglichen es die Funktionen malloc() und calloc() aus der Standard-Bibliothek stdlib.h, nach Möglichkeit ein entsprechend großes Stück an freiem Speicherplatz (“memory”) zu finden und für das Feld zu reservieren (“allocate”).

Der Speicher eines Programms setzt sich allgemein zusammen aus einem Teil namens “Stack”, der für statische Variablen reserviert ist, und einem dynamischen Teil namens “Heap”, auf den mittels malloc() oder calloc() zugegriffen werden kann.

Bei der Verwendung dieser Funktionen kann valgrind als “Debugger” für dynamischen Speicherplatz eingesetzt werden.

Die Funktion malloc()

Als Ergebnis gibt die Funktion malloc() einen Zeiger auf die nutzbare Speicheradresse zurück, oder NULL, falls keine Speicherreservierung möglich war. Bei jeder neuen Speicherreservierung sollte der Rückgabewert geprüft und gegebenenfalls eine Fehlermeldung ausgegeben werden. Im erfolgreichen Fall hat der zurück gegebene Zeiger den Typ void * und wird üblicherweise vom Programmierer mittels des cast-Operators in einen Zeiger vom gewünschten Typ umgewandelt.

Um beispielsweise einen dynamischen Speicherplatz für ein Array mit 50 int-Werten zu erhalten, kann man folgendes eingeben:

numbers = (int *) malloc(50 * sizeof (int));

An die Funktion malloc() wird allgemein die zu reservierende Speichergröße in Bytes als Argument übergeben; für beispielsweise 50 Werte vom Datentyp int ist damit auch das Fünfzigfache der Größe dieses Datentyps nötig. Der Rückgabewert von malloc(), nämlich void *, wird mit Hilfe des Casts (int *) in einen Zeiger auf int umgewandelt.

Wird der Speicher nicht mehr benötigt, so muss er manuell mittels free() wieder freigegeben werden. Als Argument wird dabei der Name des variablen Speichers angegeben, also beispielsweise free(numbers). In C gibt es keinen “Garbage Collector”, der nicht mehr benötigte Speicherbereiche automatisch wieder freigibt; es ist also Aufgabe des Programmierers dafür zu sorgen, dass Speicher nach dem Gebrauch wieder freigegeben wird und somit kein Speicherleck entsteht.

Die Funktion calloc()

Neben der Funktion malloc() gibt es in der Standardbibliothek stdlib.h eine weitere Funktion zur dynamischen Speicherreservierung namens calloc(). Beim Aufruf dieser Funktion wird als erstes Argument die Anzahl der benötigten Variablen, als zweites Argument die Größe einer einzelnen Variablen in Bytes angegeben. Bei einer erfolgreichen Reservierung wird, wie bei malloc(), ein void *-Zeiger auf den reservierten Speicher zurückgegeben, andernfalls NULL. Der Unterschied zwischen malloc() und calloc() liegt darin, dass calloc() alle Bits im Speicherbereich auf 0 setzt und dadurch sicherstellt, dass zuvor mit free() freigegebene Daten zufällig weiterverarbeitet werden.

Auch bei der Verwendung von calloc() muss Speicher, der nicht mehr benötigt wird, manuell mittels free() wieder freigegeben werden.

Die Funktion realloc()

Mit der Funktion realloc() kann ein mit malloc() oder calloc() reservierter Speicherbereich nachträglich in seiner Größe verändert werden.

Als erstes Argument gibt man bei realloc() einen Zeiger auf einen bereits existierenden dynamischen Speicherbereich an, als zweites die gewünschte neue Größe des Speicherbereichs. Kann der angeforderte Speicher nicht an der bisherigen Adresse angelegt werden, weil dort kein ausreichend großer zusammenhängender Speicherbereich mehr frei ist, dann verschiebt realloc() den vorhandenen Speicherbereich an eine andere Stelle im Speicher, an der noch genügend Speicher frei ist.

numbers = (int *) realloc(numbers, 100 * sizeof (int));

Als Ergebnis gibt die Funktion realloc() ebenfalls einen void *-Zeiger auf den reservierten Speicherbereich zurück, wenn die Speicherreservierung erfolgreich war, andernfalls NULL. Übergibt man an realloc() einen NULL-Pointer als Adresse, so ist realloc() mit malloc() identisch und gibt einen Zeiger auf einen neu erstellten dynamischen Speicherbereich zurück.

memcmp() und strcmp() – Vergleiche von Feldern

In C kann man den Inhalt zweier Felder nicht direkt vergleichen, es kann hierfür also nicht array_1 == array_2 geschrieben werden. Bei diesem Test würden lediglich, da der Name eines Feldes auf das erste im Feld gespeicherte Element verweist, die Speicheradressen zweier Variablen verglichen werden, jedoch nicht deren Inhalt.

Für einen inhaltlichen Vergleich müssen alle Einzelelemente der Felder miteinander verglichen werden. Dies kann automatisch mit der Funktion memcmp() aus der Standardbibliothek string.h durchgeführt werden. Bei identischen Feldern wird der Wert 0 als Ergebnis zurückgegeben. Stößt die Funktion im ersten Feld auf einen Wert, der größer ist als im zu vergleichenden Feld, so wird ein positiver Wert >0 zurückgegeben, im umgekehrten Fall ein negativer Wert <0.

Handelt es sich bei den Feldern um Zeichenketten, so sollte anstelle von memcmp() bevorzugt die Funktion strcmp() verwendet werden. Diese prüft ebenfalls Zeichen für Zeichen, ob die beiden angegebenen Zeichenketten übereinstimmen. Anders als bei memcmp() wird jedoch das Überprüfen der Feldinhalte beendet, sobald das String-Ende-Zeichen \0 erreicht wird. Mögliche Inhalte der Felder hinter diesem Zeichen werden somit nicht verglichen.

memcpy() und strcpy() – Kopieren von Feldern

Der Funktion strcpy() wird als erstes Argument der Name des Zielstrings, als zweites Argument eine dorthin zu kopierende Zeichenkette übergeben:

char target_string[50];

strcpy(target_string, "Hallo Welt!");

puts(target_string);
// Ergebnis: "Hallo Welt!"

Der Zielstring wird von strcpy() automatisch mit dem Zeichenkette-Ende-Zeichen '\0' abgeschlossen. Wichtig ist zu beachten, dass strcpy() nicht prüft, ob der Zielstring ausreichend groß ist; reicht der Platz dort nicht aus, werden die Bytes einer anschließend im Speicher abgelegten Variablen überschrieben, was unvorhersehbare Fehler mit sich bringen kann. Als Programmierer muss man somit entweder selbst darauf achten, dass nicht Zielstring ausreichend groß ist, oder die Funktion strncpy() verwenden, welcher als drittes Argument die Anzahl n der zu kopierenden Zeichen übergeben wird.

strcat() – Verknüpfen von Zeichenketten

Der Funktion strcat() wird als erstes Argument der Name des Zielstrings, als zweites Argument eine dort anzufügenden Zeichenkette übergeben:

char target_string[50];

strcpy(target_string, "Hallo Welt!");;
strcat(target_string, " Auf Wiedersehen!");

puts(target_string);
// Ergebnis: "Hallo Welt! Auf Wiedersehen!"

strcat() überschreibt automatisch das Zeichenkette-Ende-Zeichen '\0' des Zielstring mit dem ersten Zeichen des anzuhängenden Strings und schließt nach dem Anfügen der restlichen Zeichen den Zielstring wiederum mit '\0' ab.

Ebenso wie bei strcpy() muss auch bei Verwendung von strcat() auf einen ausreichend grossen Zielstring geachtet werden. Als Alternativ kann die Funktion strncat() verwendet werden, der als drittes Argument eine Anzahl n an anzuhängenden Zeichen übergeben wird.