vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 2

Tag 9

Zeichen und Strings

Ein Zeichen ist ein einzelner Buchstabe, eine Ziffer, ein Satzzeichen oder ein beliebiges anderes Symbol. Unter einem String versteht man eine beliebige Folge von Zeichen. Strings dienen dazu, Textdaten aufzunehmen, die aus Buchstaben, Ziffern, Satzzeichen und anderen Symbolen bestehen. Zweifelsohne können Zeichen und Strings in vielen Programmanwendungen extrem nützlich sein. Heute lernen Sie:

Der Datentyp char

C verwendet für die Aufnahme von Zeichen den Datentyp char. Wie Sie am Tag 2, »Die Komponenten eines C-Programms: Quellcode und Daten«, gesehen haben, gehört char zu den numerischen Integer-Datentypen von C. Wenn aber char vom Typ her nummerisch ist, wie kann er dann dazu verwendet werden, Zeichen aufzunehmen?

Um diese Frage zu beantworten, muss man sich anschauen, wie in C Zeichen gespeichert werden. Im Speicher Ihres Computers sind alle Daten in numerischer Form abgelegt. Es gibt keinen direkten Weg, Zeichen zu speichern. Es gibt jedoch für jedes Zeichen einen numerischen Code. Dieser wird ASCII-Code oder ASCII- Zeichensatz genannt (ASCII steht für American Standard Code for Information Interchange, auf gut Deutsch: »amerikanischer Standardcode für den Informationsaustausch«). Dieser Code weist den Groß- und Kleinbuchstaben, den Ziffern, Satzzeichen und anderen Symbolen die Werte von 0 bis 255 zu. Sie finden den ASCII-Zeichensatz in Anhang A.

So stellt zum Beispiel die Zahl 97 den ASCII-Code für den Buchstaben a dar. Wenn Sie also das Zeichen a in einer Variablen vom Typ char speichern, speichern Sie eigentlich den Wert 97. Da der zulässige Zahlenbereich für den Datentyp char mit dem Standard-ASCII-Zeichensatz übereinstimmt, ist char optimal zum Speichern von Zeichen geeignet.

Vielleicht sind Sie noch etwas verwirrt? Wenn C Zeichen als Zahlen speichert, woher weiß dann Ihr Programm, ob eine gegebene Variable vom Typ char ein Zeichen oder eine Zahl ist? Wie Sie später noch lernen werden, reicht es nicht, eine Variable vom Typ char zu deklarieren; es wird noch etwas mehr verlangt:

Damit konnten Sie einen kleinen Eindruck gewinnen, wie in C ein numerischer Datentyp Zeichen speichert. Jetzt können wir zu den Details übergehen.

Zeichenvariablen

char-Variablen müssen, wie andere Variablen auch, vor ihrer Verwendung deklariert werden. Sie können bei der Deklaration initialisiert werden. Hier einige Beispiele:

char a, b, c;        /* Deklariert 3 nicht-initialsierte char-Variablen */
char code = 'x'; /* Deklariert eine char-Variable namens code */
/* und speichert in ihr den Wert x */
code = '!'; /* Speichert ! in der Variablen namens code */

Um eine literale Zeichenkonstante zu erzeugen, setzen Sie das betreffende Zeichen in einfache Anführungszeichen. Der Compiler übersetzt literale Zeichenkonstanten automatisch in den entsprechenden ASCII-Code, so dass der Variablen der numerische Code zugewiesen wird.

Symbolische Zeichenkonstanten können Sie entweder mit Hilfe der #define-Direktive oder mit dem Schlüsselwort const einrichten:

#define EX 'x'
char code = EX; /* Setzt code gleich 'x' */
const char A = 'Z';

Nachdem Sie jetzt wissen, wie man Zeichenvariablen deklariert und initialisiert, werde ich Ihnen ein Beispiel zeigen. Listing 9.1 veranschaulicht mit Hilfe der Funktion printf(), die Sie bereits am Tag 6, »Grundlagen der Ein- und Ausgabe«, kennen gelernt haben, wie »nummerisch« die Speicherung von Zeichen ist. Mit der Funktion printf() können sowohl Zeichen als auch Zahlen ausgegeben werden. Der Formatstring %c weist printf() an, ein Zeichen auszugeben, während mit %d ein Dezimalinteger ausgegeben wird. Listing 9.1 initialisiert zwei Variablen vom Typ char und gibt beide aus, einmal als Zeichen und einmal als Zahl.

Listing 9.1: Die numerische Natur der Variablen vom Typ char.

1:  /* Beispiel für die numerische Natur von char-Variablen */
2:
3: #include <stdio.h>
4:
5: /* Deklariert und initialisiert zwei char-Variablen */
6:
7: char c1 = 'a';
8: char c2 = 90;
9:
10: int main(void)
11: {
12: /* Gibt Variable c1 erst als Zeichen, dann als Zahl aus */
13:
14: printf("\nAls Zeichen lautet Variable c1: %c", c1);
15: printf("\nAls Zahl lautet Variable c1: %d", c1);
16:
17: /* Das gleiche für Variable c2 */
18:
19: printf("\nAls Zeichen lautet Variable c2: %c", c2);
20: printf("\nAls Zahl lautet Variable c2: %d\n", c2);
21:
22: return 0;
23: }

Als Zeichen lautet Variable c1: a
Als Zahl lautet Variable c1: 97
Als Zeichen lautet Variable c2: Z
Als Zahl lautet Variable c2: 90

Was Sie tun sollten

Was nicht

Verwenden Sie %c , um den Zeichenwert einer Zahl auszugeben.

Verwenden Sie einfache Anführungszeichen, wenn Sie eine Zeichenvariable initialisieren.

Verwenden Sie zur Initialisierung von Zeichenvariablen keine doppelten Anführungszeichen.

Wenn Sie mit Zeichen arbeiten, können Sie in der Regel davon ausgehen, dass die Werte von 0 bis 127 immer die gleichen Zeichen codieren. Bei der Auswahl der Zeichen, die durch die Zahlen von 0 bis 127 codiert werden, wurden allerdings vor allem die Zeichen der englischen Sprache berücksichtig. Zur Unterstützung der Zeichen anderer Sprachen (beispielsweise auch der deutschen Umlaute) gibt es etliche Erweiterungen (Zeichenwerte größer als 128). Stellen Sie aber bitte keine Vermutungen darüber an, welche Zeichen durch die erweiterten Zeichenwerte codiert werden - diese variieren von Computer zu Computer, je nach installierter Zeichentabelle.

Strings verwenden

Variablen vom Typ char können nur ein einziges Zeichen aufnehmen. Deshalb sind sie auch nur begrenzt einsetzbar. Darüber hinaus benötigen Sie eine Möglichkeit, um Strings - das heißt, eine Folge von Zeichen - zu speichern. Name und Adresse einer Person sind zum Beispiel Strings. Da es keinen besonderen Datentyp für Strings gibt, behandelt C diese Art von Information als Arrays von Zeichen.

Arrays von Zeichen

Um zum Beispiel einen String von sechs Zeichen unterzubringen, müssen Sie ein Array vom Typ char mit sieben Elementen deklarieren. Arrays vom Typ char werden wie alle anderen Arrays auch deklariert. So deklariert zum Beispiel die Anweisung

char string[10];

ein Array vom Typ char mit zehn Elementen. Dieses Array kann einen String von neun oder weniger Zeichen aufnehmen.

»Warten Sie«, werden Sie sagen, »es ist ein Array mit zehn Elementen. Warum kann es nur neun Zeichen aufnehmen?« Ein C-String ist definiert als eine Sequenz von Zeichen, die mit einem Nullzeichen endet. Ein Nullzeichen ist ein besonderes Zeichen, das durch \0 dargestellt wird. Obwohl es im Code durch zwei Zeichen (Backslash und Null) dargestellt wird, ist das Nullzeichen ein einzelnes Zeichen, das den ASCII-Wert 0 hat. Es ist eines der Escape-Sequenzen von C, die bereits am Tag 6 besprochen wurden.

Wenn ein C-Programm zum Beispiel den String Alabama speichert, speichert es die sieben Zeichen A, l, a, b, a, m und a, gefolgt von dem Nullzeichen \0 - was insgesamt acht Zeichen macht. Demzufolge kann ein Zeichenarray nur Zeichenstrings aufnehmen, die um eins kleiner sind als die Gesamtzahl der Elemente im Array.

Zeichenarrays initialisieren

Wie andere Datentypen in C können Zeichenarrays bei ihrer Deklaration initialisiert werden. Den Zeichenarrays können wie folgt Element für Element Werte zugewiesen werden:

char string[10] = { 'A', 'l', 'a', 'b', 'a', 'm', 'a', '\0' };

Es ist jedoch wesentlich bequemer, einen literalen String zu verwenden. Darunter verstehen wir eine Folge von Zeichen in doppelten Anführungszeichen:

char string[10] = "Alabama";

Wenn Sie einen literalen String in Ihrem Programm verwenden, hängt der Compiler automatisch das abschließende Nullzeichen an das Ende des Strings. Wenn Sie bei der Deklaration des Arrays die Anzahl der Indizes nicht angeben, errechnet der Compiler für Sie sogar noch die erforderliche Größe des Arrays. Die folgende Zeile erzeugt und initialisiert ein Array mit acht Elementen:

char string[] = "Alabama";

Denken Sie immer daran, dass Strings ein abschließendes Nullzeichen erfordern. Alle C-Funktionen zur Stringbearbeitung (die am Tag 16, »Stringmanipulation«, besprochen werden) ermitteln die Länge eines übergebenen Strings dadurch, dass sie nach dem Nullzeichen suchen. Diese Funktionen haben keine andere Möglichkeit, das Ende des Strings zu erkennen. Wenn das Nullzeichen fehlt, geht Ihr Programm davon aus, dass der String sich bis zum nächsten Nullzeichen im Speicher erstreckt. Wenn Sie das Nullzeichen vergessen, kann dies ziemlich eklige Programmfehler verursachen.

Strings und Zeiger

Sie haben gelernt, dass Strings in Arrays vom Typ char gespeichert werden und das Ende eines Strings (der nicht das gesamte Array belegen muss) durch ein Nullzeichen markiert ist. Da das Ende eines Strings bereits markiert ist, benötigt man zur vollständigen Definition eines Strings eigentlich nur noch etwas, das auf den Anfang des Strings zeigt. (Ist zeigt hier das richtige Wort? Ich glaube ja!)

Vielleicht ahnen Sie schon dank des obigen Hinweises, worauf dieser Abschnitt abzielt. Wie Sie von Tag 8, »Zeiger«, wissen, ist der Name eines Arrays ein Zeiger auf das erste Element im Array. Deshalb benötigen Sie für den Zugriff auf einen String, der in einem Array gespeichert ist, nur den Array-Namen. Diese Methode ist in C der übliche Weg, um auf einen String zuzugreifen.

So unterstützen beispielsweise auch die C-Bibliotheksfunktionen den Zugriff über den Array-Namen. Die C-Standardbibliothek enthält eine Reihe von Funktionen zur Manipulation von Strings. (Diese Funktionen werden am Tag 16 ausführlich behandelt.) Um einer dieser Funktionen einen String zu übergeben, übergeben Sie den Array-Namen. Dies gilt auch für die beiden Funktionen zur Ausgabe von Strings, printf() und puts(). Hierauf werden wir später in diesem Kapitel noch eingehen.

Vielleicht ist Ihnen aufgefallen, dass ich oben von »Strings, die in einem Array gespeichert werden« gesprochen habe. Soll damit angedeutet werden, dass es Strings gibt, die nicht in Arrays gespeichert werden? Die Antwort lautet ja, und der nächste Abschnitt soll dies erläutern.

Strings ohne Arrays

Aus den vorangehenden Abschnitten wissen Sie, dass ein String durch den Namen eines Zeichenarrays und ein Nullzeichen definiert ist. Der Array-Name ist ein Zeiger vom Typ char auf den Anfang des Strings. Die Null markiert das Stringende. Der eigentliche Platz, der von dem String in einem Array belegt wird, ist nebensächlich. Ehrlich gesagt, dient das Array lediglich dazu, einen allokierten Speicherbereich für den String bereitzustellen.

Was wäre, wenn Sie, ohne ein Array zu allokieren, genügend Speicherbereich finden würden? Sie könnten dann in diesem Speicherbereich einen String mit seinem abschließenden Nullzeichen ablegen. Ein Zeiger auf das erste Zeichen würde - wie bei einem String in einem Array - dazu dienen, den Anfang des Strings zu kennzeichnen. Wie aber kann man feststellen, wo genügend Speicherplatz vorhanden ist? Es gibt zwei Möglichkeiten: Entweder man allokiert Speicher für einen literalen String (die Speicherreservierung erfolgt dann bei der Kompilation des Programms) oder man bedient sich der Funktion malloc(), um während der Programmausführung Speicher zu reservieren. Den letzteren Weg bezeichnet man auch als dynamische Speicherreservierung.

Stringspeicher zur Kompilierzeit zuweisen

Der Beginn eines Strings wird, wie bereits erwähnt, durch einen Zeiger auf eine Variable vom Typ char angezeigt. Vielleicht erinnern Sie sich noch, wie ein solcher Zeiger deklariert wird:

char *botschaft;

Diese Anweisung deklariert einen Zeiger namens botschaft, der auf Variablen vom Typ char zeigen kann. So deklariert, weist der Zeiger noch auf keinen definierten Speicherbereich. Was aber, wenn Sie die Zeigerdeklaration wie folgt ändern:

char *botschaft = "Der Geist des großen Cäsar!";

Wenn diese Anweisung kompiliert wird, wird der String »Der Geist des großen Cäsar!« (inklusive abschließenden Nullzeichens) irgendwo im Speicher abgelegt, und der Zeiger botschaft zeigt nach der Initialisierung auf das erste Zeichen des Strings. Kümmern Sie sich nicht darum, wo genau im Speicher der String liegt - dies ist Aufgabe des Compilers. Nach seiner Definition ist botschaft ein Zeiger auf den String und kann als solcher verwendet werden.

Die obige Deklaration/Initialisierung entspricht der folgenden Deklaration. Auch die beiden Notationen *botschaft und botschaft[] sind äquivalent. Beide bedeuten »ein Zeiger auf«.

char botschaft[] = "Der Geist des großen Cäsar!";

Diese Methode, Speicher für die Aufnahme von Strings zu reservieren, ist praktisch, wenn Sie beim Schreiben des Programms wissen, was Sie benötigen. Was aber, wenn das Programm aufgrund von Benutzereingaben oder anderen Faktoren, die beim Erstellen des Programms noch unbekannt sind, unterschiedlichen Speicherplatz für seine Strings benötigt? In solchen Fällen verwendet man die Funktion malloc(), mit der man Speicherplatz zur Laufzeit reservieren kann.

Die Funktion malloc()

Die malloc()-Funktion ist eine der C-Funktionen zur Speicherallokation. Wenn Sie malloc() aufrufen, übergeben Sie der Funktion die Anzahl der benötigten Speicherbytes. malloc() sucht und reserviert einen Speicherblock der erforderlichen Größe und gibt die Adresse des ersten Byte im Block zurück. Wie der Speicher gefunden wird, braucht Sie nicht zu kümmern - das ist Aufgabe der Funktion malloc() und des Betriebssystems.

Die malloc()-Funktion liefert eine Adresse zurück, und zwar als Zeiger auf void. Warum void? Ein Zeiger vom Typ void zu mit allen Datentypen kompatibel. Da der von malloc() reservierte Speicher zur Speicherung von Daten beliebiger Datentypen verwendet wird, ist der Rückgabetyp void korrekt.

Die Funktion malloc()

#include <stdlib.h>
void *malloc(size_t groesse);

malloc() reserviert einen Speicherblock, der die in groesse angegebene Anzahl von Bytes umfasst. Wenn Sie Speicher mit Hilfe von malloc() bei Bedarf reservieren, anstatt gleich bei Programmbeginn großzügig Speicher für zukünftige Aufgaben zu reservieren, können Sie den Arbeitsspeicher des Rechners effizienter nutzen. Für die Verwendung von malloc() müssen Sie die Header-Datei stdlib.h einbinden.

malloc() liefert einen Zeiger auf den reservierten Speicherblock zurück. Wenn malloc() den erforderlichen Speicherplatz nicht reservieren kann, liefert die Funktion NULL zurück. Aus diesem Grund sollten Sie immer, wenn Sie Speicher zuweisen, den Rückgabewert testen, auch wenn der Speicher, der zugewiesen werden soll, klein ist. Wir werden NULL und seine Anwendung in Kürze diskutieren.

Beispiel 1

#include <stdlib.h>
#include <stdio.h>
int main(void)
{
/* Speicher für einen String mit 100 Zeichen reservieren */
char *str;
if (( str = (char *) malloc(100)) == NULL)
{
printf( "Nicht genug Speicher, um den Puffer zu allokieren\n");
exit(1);
}
printf( "Speicher für den String wurde reserviert!\n" );
return 0;
}

Beispiel 2

/* Speicher für ein Array mit 50 Integer reservieren */
int *zahlen;
zahlen = (int *) malloc(50 * sizeof(int));

Beispiel 3

/* Speicher für ein Array mit 10 float-Werten reservieren */
float *zahlen;
zahlen = (float *) malloc(10 * sizeof(float));

Einsatz der malloc()-Funktion

Sie können mit malloc() einen Speicherplatz für ein einziges Zeichen vom Typ char reservieren. Deklarieren Sie dazu zuerst einen Zeiger auf den Typ char:

char *zgr;

Danach rufen Sie die Funktion malloc() auf und übergeben ihr die Größe des gewünschten Speicherblocks. Da ein Zeichen in der Regel nur ein Byte belegt, benötigen Sie lediglich einen Block von einem Byte. Der von malloc() zurückgelieferte Wert wird dann dem Zeiger zugewiesen:

zgr = malloc(1);

Diese Anweisung reserviert einen Speicherblock von einem Byte und weist dessen Adresse zgr zu. Im Gegensatz zu den Variablen, die in dem Programm deklariert sind, besitzt dieses Speicher-Byte keinen Namen - es bildet ein namenloses Speicherobjekt, auf das man nur über den Zeiger zugreifen kann. Um zum Beispiel in dem Speicherobjekt das Zeichen 'x' zu speichern, würde man schreiben:

*zgr = 'x';

In der gleichen Weise, in der man mit malloc() Speicher für eine Variable vom Typ char reserviert, kann man mit malloc() auch Speicher für einen String allokieren. Der Hauptunterschied liegt darin, dass Sie berechnen müssen, wie viel Speicher reserviert werden muss - das heißt, Sie müssen die maximale Anzahl der Zeichen im String kennen. Dies Maximum hängt von den Bedürfnissen Ihres Programms ab. Angenommen Sie wollten für dieses Beispiel einen Speicherbereich für einen String von 99 Zeichen plus dem abschließenden Nullzeichen (also insgesamt 100 Zeichen) zuweisen. Dann deklarieren Sie zuerst einen Zeiger auf den Typ char und rufen danach malloc() wie folgt auf:

char *zgr;
zgr = malloc(100);

Jetzt zeigt zgr auf einen reservierten Block von 100 Byte, der zur Speicherung und Manipulation eines Strings verwendet werden kann. Für die Arbeit mit zgr ist es unerheblich, ob der zugehörige Speicher mit malloc() oder im Zuge einer Array- Deklaration reserviert und zugewiesen wurde.

char zgr[100];

Mit Hilfe von malloc() können Sie Speicher nach Bedarf reservieren. Dabei versteht es sich von selbst, dass der verfügbare Speicherbereich nicht unbegrenzt ist. Wie viel Speicher zur Verfügung steht, hängt davon ab, wie viel Speicher Sie in Ihrem Rechner installiert haben und wie viel Platz vom Betriebssystem und den laufenden Programmen belegt wird. Wenn nicht genug Speicher verfügbar ist, liefert malloc() eine nicht initialisierte Adresse (das heißt NULL) zurück. Ihr Programm sollte den Rückgabewert von malloc() daher stets überprüfen, um sicherzustellen, dass der erforderliche Speicherbereich auch erfolgreich zugewiesen wurde. Vergleichen Sie den Rückgabewert von malloc() mit der symbolischen Konstanten NULL, die in stdlib.h definiert ist. Listing 9.2 veranschaulicht den Einsatz von malloc(). Jedes Programm, das von malloc() Gebrauch macht, muss die Header-Datei stdlib.h mit #include einbinden.

Viele C-Programmierer weisen allen Zeigern, die nicht anderweitig initialisiert wurden, einen speziellen Wert zu. Dies ist der Wert NULL, der innerhalb des Systems tatsächlich eine Sonderstellung einnimmt. Indem Sie einem Zeiger NULL zuweisen, sorgen Sie dafür, dass der Zeiger nirgendwohin zeigt. In den meisten Betriebssystemen, einschließlich Linux, lautet dieser Wert Null und ist für die Benutzung durch das Betriebssystem reserviert, das heißt, er wäre normalerweise von keinem besonderen Nutzen.

Das Schöne an dieser Vorgehensweise ist, dass Sie feststellen können, ob ein Zeiger initialisiert wurde oder nicht, indem Sie prüfen, ob er gleich NULL ist. Der Wert NULL ist in stdio.h sowie in stdlib.h deklariert, so dass Sie eine dieser Header-Dateien einbinden müssen, wenn Sie NULL verwenden wollen.

Listing 9.2: Mit der malloc()-Funktion Speicher für String-Daten reservieren.

1:  /* Beispiel für die Verwendung von malloc() zur */
2: /* Speicherreservierung für String-Daten. */
3:
4: #include <stdio.h>
5: #include <stdlib.h>
6:
7: char count, *zgr, *z;
8:
9: int main(void)
10: {
11: /* Reserviert einen Block von 35 Bytes. Testet auf Erfolg. */
12:
13:
14: zgr = malloc(35 * sizeof(char));
15:
16: if (zgr == NULL)
17: {
18: puts("Fehler bei der Speicherzuweisung.");
19: return 1;
20: }
21:
22: /* Füllt den String mit Werten von 65 bis 90, */
23: /* was den ASCII-Codes von A-Z entspricht. */
24:
25: /* z ist ein Zeiger, mit dem der String durchlaufen wird. */
26: /* zgr soll weiterhin unverändert auf den Anfang */
27: /* des Strings zeigen. */
28:
29: z = zgr;
30:
31: for (count = 65; count < 91 ; count++)
32: *z++ = count;
33:
34: /* Fügt das abschließende Nullzeichen ein. */
35:
36: *z = '\0';
37:
38: /* Zeigt den String auf dem Bildschirm an. */
39:
40: puts(zgr);
41:
42: return 0;
43: }

ABCDEFGHIJKLMNOPQRSTUVWXYZ

Dieses Programm zeigt ein einfaches Beispiel für den Einsatz von malloc(). Das Programm selbst scheint zwar lang, besteht aber zu einem großen Teil aus Kommentaren, die in den Zeilen 1, 2, 11, 22 bis 27, 34 und 38 detailliert darüber informieren, was das Programm macht. Zeile 5 bindet die für malloc() notwendige Header-Datei stdlib.h und Zeile 4 die für die puts()-Funktionen notwendige Header- Datei stdio.h ein. Zeile 7 deklariert zwei Zeiger und eine Zeichenvariable, die später im Listing eingesetzt werden. Keine dieser Variablen wird initialisiert. Deshalb sollten Sie (noch!) nicht verwendet werden.

Die malloc()-Funktion wird in Zeile 14 aufgerufen. Übergeben wird ihr der Wert 35 multipliziert mit der Größe eines char. Hätten Sie auch nur 35 übergeben können? Ja, doch dann würden Sie davon ausgehen, dass jeder Benutzer Ihres Programms einen Computer hat, der Variablen vom char-Typ in einem Byte abspeichert. Am Tag 2 wurde bereits erwähnt, dass verschiedene Compiler unterschiedliche Variablengrößen verwenden können. Mit dem sizeof-Operator können Sie Code schreiben, der portabel ist.

Gehen Sie nie davon aus, dass malloc() den von Ihnen gewünschten Speicher auch reservieren kann, denn im Grunde ist Ihr Allokations-Befehl lediglich eine Anfrage. Zeile 16 zeigt Ihnen, wie Sie am einfachsten überprüfen können, ob malloc() Speicher bereitstellen konnte. Wenn der Speicher zugewiesen wurde, zeigt zgr darauf, andernfalls ist zgr gleich NULL. Wenn es dem Programm nicht gelingt, Speicher zu finden, geben die Zeilen 18 und 19 eine Fehlermeldung aus und sorgen für einen würdigen Programmabbruch.

Zeile 29 initialisiert den anderen Zeiger, z, der in Zeile 7 deklariert wurde. Diesem wird der gleiche Adresswert zugewiesen wie zgr. Eine for-Schleife verwendet diesen neuen Zeiger, um in dem reservierten Speicher Werte abzulegen. Wenn Sie sich Zeile 31 anschauen, sehen Sie, dass count mit 65 initialisiert und dann in jedem Schleifendurchgang um 1 inkrementiert wird, bis der Wert 91 erreicht ist. Bei jedem Durchlauf der for-Schleife wird der Wert von count an der Adresse abgelegt, auf die z gerade zeigt. Beachten Sie, dass jede Inkrementierung von count mit einer Inkrementierung der Adresse, auf die z zeigt, einhergeht. Das bedeutet, dass alle Werte hintereinander im Speicher abgelegt werden.

Es sollte Ihnen aufgefallen sein, dass hier der Variablen count, die vom Typ char ist, Zahlen zugewiesen werden. Erinnern Sie sich noch an die Diskussion über die ASCII- Zeichen und ihre numerischen Entsprechungen? Die Zahl 65 entspricht A, 66 entspricht B, 67 entspricht C und so weiter. Die for-Schleife wird beendet, nachdem das gesamte Alphabet in dem reservierten Speicherbereich abgelegt wurde. Zeile 36 schließt das Schreiben der Zeichenwerte ab, indem eine Null in die letzte Adresse, auf die z zeigt, geschrieben wird. Durch das Anhängen der Null können Sie die Werte nun als String verwenden. Doch denken Sie daran, dass der Zeiger zgr immer noch auf den ersten Wert, A, verweist. Wenn Sie den Zeiger als String verwenden, werden alle Zeichen bis zur Null ausgegeben. Zeile 40 verwendet puts(), um diesen Punkt zu prüfen und das Ergebnis unserer Bemühungen auszugeben.

Was Sie nicht tun sollten

Reservieren Sie nicht mehr Speicher, als Sie benötigen. Nicht jeder verfügt über einen großzügigen Arbeitsspeicher. Sie sollten deshalb sparsam damit umgehen.

Versuchen Sie nicht, einem Zeichenarray, das lediglich mit Speicher für einen kleineren String verbunden wurde, einen neuen, längeren String zuzuweisen. So zeigt ein_string nach der folgenden Deklaration:

char ein_string[] = "JA";

auf "JA". Wenn Sie versuchen, diesem Array "NEIN" zuzuweisen, könnte das schwerwiegende Fehler nach sich ziehen. Das Array ist ursprünglich nur dafür gedacht, drei Zeichen aufzunehmen - 'J', 'A' und eine Null. "NEIN" ist fünf Zeichen lang - 'N', 'E', 'I','N' und eine Null. Und Sie wissen nicht, was mit dem vierten und fünften Zeichen überschrieben wird.

Strings und Zeichen anzeigen

Wenn Ihr Programm mit Strings arbeitet, wird es diese wahrscheinlich irgendwann auf dem Bildschirm ausgeben müssen. Dies geschieht in der Regel mit den Funktionen puts() oder printf().

Die Funktion puts()

Die Bibliotheksfunktion puts() ist Ihnen bereits in einigen Programmen dieses Buches begegnet. Mit dieser Funktion können Sie einen String auf dem Bildschirm ausgeben. puts() übernimmt als einziges Argument einen Zeiger auf den auszugebenden String. Da ein literaler String als ein Zeiger auf einen String zu betrachten ist, kann man mit puts() sowohl literale Strings als auch String-Variablen ausgeben. Die puts()- Funktion fügt automatisch am Ende jedes ausgegebenen Strings ein Neue-Zeile- Zeichen an, so dass jeder weitere mit puts() ausgegebene String in einer eigenen Zeile steht.

Listing 9.3 gibt ein Beispiel für die Verwendung von puts().

Listing 9.3: Mit der Funktion puts() Text auf dem Bildschirm ausgeben.

1: /* Beispiel für die Ausgabe von Strings mit puts(). */
2:
3: #include <stdio.h>
4:
5: char *meldung1 = "C";
6: char *meldung2 = "ist ";
7: char *meldung3 = "die";
8: char *meldung4 = "beste";
9: char *meldung5 = "Programmiersprache!!";
10:
11: int main(void)
12: {
13: puts(meldung1);
14: puts(meldung2);
15: puts(meldung3);
16: puts(meldung4);
17: puts(meldung5);
18:
19: return 0;
20: }

C
ist
die
beste
Programmiersprache!!

Dies Listing lässt sich ziemlich einfach nachvollziehen. Für die Standardausgabefunktion puts() muss die Header-Datei stdio.h eingebunden werden, was in Zeile 3 geschieht. Die Zeilen 5 bis 9 deklarieren und initialisieren fünf verschiedene Variablen. Jede dieser Variablen ist ein Zeichenzeiger, eine Stringvariable. Die Zeilen 13 bis 17 verwenden die puts()-Funktion, um jeden String auszugeben.

Die Funktion printf()

Sie können Strings aber auch mit der Bibliotheksfunktion printf() ausgeben. Am Tag 6 wurde bereits beschrieben, wie printf() seine Ausgabe mit Hilfe eines Formatstrings und verschiedener Konvertierungsspezifizierer in Form bringt. Für die Ausgabe von Strings verwendet man den Konvertierungsspezifizierer %s.

Wenn printf() in seinem Formatstring auf ein %s trifft, ersetzt sie %s durch das zugehörige Argument aus der Argumentliste. Für Strings muss dieses Argument ein Zeiger auf den auszugebenden String sein. Die printf()-Funktion gibt den String Zeichen für Zeichen auf dem Bildschirm aus und hört damit erst auf, wenn sie das abschließende Nullzeichen erreicht.

char *str = "Eine anzuzeigende Nachricht";
printf("%s", str);

Sie können auch mehrere Strings ausgeben oder die Strings zusammen mit literalem Text und/oder numerischen Variablen ausgeben:

char *bank = "Sparkasse";
char *name = "Hans Schmidt";
int konto = 1000;
printf("Das Konto von %s bei der %s steht auf %d DM.",name,bank,konto);

Die Ausgabe lautet:

Das Konto von Hans Schmidt bei der Sparkasse steht auf 1000 DM.

Damit haben Sie jetzt erst einmal ausreichend Informationen an der Hand, um Strings auszugeben. Eine vollständige Beschreibung der Funktion printf() finden Sie am Tag 13, »Mit Bildschirm und Tastatur arbeiten«.

Strings von der Tastatur einlesen

Oft müssen Programme Strings nicht nur ausgeben, sondern auch vom Benutzer über die Tastatur eingegebene Strings aufnehmen. In der C-Bibliothek stehen dafür drei Funktionen zur Verfügung - gets(), fgets() und scanf(). Hiermit habe ich alle erwähnt, doch möchte ich Ihnen von der Verwendung von gets() abraten, da diese Funktion einen schwerwiegenden Fehler enthält. Glücklicherweise können Sie ohne Bedenken fgets() anstelle von gets() verwenden.

Bevor Sie einen String von der Tastatur einlesen, müssen Sie einen Ort schaffen, wo Sie ihn ablegen können. Um Speicherplatz für einen String bereitzustellen, können Sie eine der beiden bereits beschriebenen Methoden verwenden - die Array- Deklaration oder die Verwendung der malloc()-Funktion.

Strings mit den Funktionen gets() und fgets() einlesen

Die Aufgabe von gets() und fgets() besteht darin, Zeichen von der Tastatur einzulesen und als String an das aufrufende Programm weiterzureichen. Es werden dabei alle Zeichen bis zum ersten Neue-Zeile-Zeichen (das durch die Eingabetaste erzeugt wird) eingelesen. Beide Funktionen schließen den String mit einem Nullzeichen ab, bevor Sie ihn an den Aufrufer zurückliefern. Der Unterschied zwischen beiden Funktionen ist, dass fgets() im Gegensatz zu gets() das Neue-Zeile- Zeichen mit aufnimmt.

Die Funktionsprototypen dieser zwei Funktionen sind in der Header-Datei stdio.h wie folgt definiert:

char *gets(char *s);
char *fgets(char *s, int size, FILE *stream);

Lassen Sie den letzten Parameter von fgets() einmal außer Acht und richten Sie Ihr Augenmerk auf den vorangehenden Parameter, in dem die Größe des Arrays s angegeben werden kann. Dieser Parameter fehlt der Funktion gets(), wodurch sie so gefährlich ist. Wenn Sie ein char-Array mit zehn Zeichen deklarieren und es an gets() übergeben, wird diese Funktion so viele Zeichen einlesen, wie der Anwender eingibt, bevor er die Eingabetaste betätigt. Das können auch mehr als zehn Zeichen sein. Folglich wird über das Ende Ihres Arrays hinausgeschrieben. Da die Funktion gets() eine potentielle Gefahrenquelle darstellt, werden wir sie in diesem Buch nicht benutzen. Wenn Sie sie aber trotzdem in einem Ihrer Programme verwenden, werden Sie wahrscheinlich feststellen, dass Sie auch vom GNU-C-Compiler vor dem Gebrauch der Funktion gewarnt werden.

In Listing 9.4 finden Sie ein Beispiel für fgets(). Der über die Tastatur eingegebene String wird an der Position gespeichert, auf die der an fgets() übergebene Zeiger auf char verweist. Die maximal zulässige Stringlänge (einschließlich des Nullzeichens), die fgets() einlesen darf, wird als zweiter Parameter übergeben. Die externe Variable stdin bildet den dritten Parameter. Diese Variable ist zusammen mit fgets() in der Header-Datei stdio.h definiert. Sie ist bereits initialisiert, und Sie sollten daran keine Änderung vornehmen, sondern die Variable einfach nur verwenden.

Listing 9.4: Stringdaten mit fgets() von der Tastatur einlesen.

1:  /* Beispiel für die Bibliotheksfunktion fgets(). */
2:
3: #include <stdio.h>
4:
5: /* Ein Zeichen-Array für die Aufnahme der Eingabe allokieren. */
6:
7: char eingabe[40];
8:
9: int main(void)
10: {
11: puts("Bitte Text eingeben und dann die Eingabetaste drücken: ");
12: fgets(eingabe, 40, stdin);
13: printf("Ihre Eingabe lautete: %s\n", eingabe);
14:
15: return 0;
16: }

Bitte Text eingeben und dann die Eingabetaste drücken:
Dies ist ein Test
Ihre Eingabe lautete: Dies ist ein Test

In diesem Beispiel lautet das erste Argument an fgets() eingabe und ist der Name eines Arrays vom Typ char und damit ein Zeiger auf das erste Array-Element. Das Array wird in Zeile 7 mit 40 Elementen deklariert, weshalb als zweites Argument der Wert 40 übergeben wird. Mit dem dritten Argument, stdin, werden wir uns am Tag 13 näher befassen.

fgets() hat auch einen Rückgabewert, der aber in dem obigen Beispiel ignoriert wurde. Dieser Rückgabewert ist ein Zeiger vom Typ char. Im Erfolgsfall weist dieser Zeiger auf die Adresse, an der der Eingabestring gespeichert ist; falls ein Fehler auftrat, wird ein NULL-Zeiger zurückgeliefert. Wenn von der Tastatur eingelesen wird, sind Fehler eher selten. In anderen Anwendungsbereichen hat der Rückgabewert von fgets() aber durchaus seine Berechtigung. Diese kommen allerdings erst in Kapitel 15, »Mit Dateien arbeiten«, zur Sprache.

Was Sie tun sollten

Was nicht

Verwenden Sie fgets() und übergeben Sie die Länge des Zeichen-Arrays als zweiten Parameter an die Funktion fgets().

Stellen Sie sicher, dass Sie als ersten Parameter einen gültigen, bereits initialisierten Zeiger an die Funktion fgets() übergeben.

Vermeiden Sie die Funktion gets(), da man nie wirklich sicher sein kann, wie viele Zeichen gets() einlesen wird, und da die Funktion gegebenenfalls über den Speicherbereich des ihr übergebenen Arrays hinaus schreibt.

Strings mit der Funktion scanf() einlesen

Am Tag 6 haben Sie gelernt, dass die Bibliotheksfunktion scanf() numerische Daten von der Tastatur einliest. Die Funktion kann aber auch Strings einlesen. Denken Sie daran, dass scanf() einen Formatstring verwendet, der ihr mitteilt, wie die Eingabe zu lesen ist. Um einen String zu lesen, müssen Sie im Formatstring von scanf() den Spezifizierer %s verwenden. Wie schon bei gets() wird auch scanf() ein Zeiger auf den Speicherbereich für den Strings übergeben.

Wie stellt scanf() fest, wo der String anfängt und wo er aufhört? Der Anfang wird durch das erste Nicht-Whitespace-Zeichen gebildet. Das Ende kann auf zweifachem Weg angegeben werden. Wenn Sie %s im Formatstring verwenden, reicht der String bis zum nächsten Whitespace-Zeichen (Leerzeichen, Tabulator oder Neue-Zeile- Zeichen). Wenn Sie %ns verwenden (wobei n eine Integer-Konstante ist, die eine Feldbreite angibt), liest scanf() die nächsten n Zeichen oder bis zum nächsten Whitespace-Zeichen ein - je nachdem, was zuerst erreicht wird.

Sie können mit scanf() auch mehrere Strings einlesen. Dazu müssen Sie mehr als ein %s in den Formatstring einbauen. Für jedes %s im Formatstring folgt scanf() den nachstehenden Regeln, um die angeforderte Anzahl von Strings in der Eingabe zu finden. Betrachten wir das Beispiel

scanf("%s%s%s", s1, s2, s3);

Wenn Ihre Eingabe zu diesem Aufruf Januar Februar März lautet, wird Januar dem String s1, Februar s2 und März s3 zugeordnet.

Und wie sieht das mit dem Spezifizierer für die Feldbreite aus? Wenn Sie die Anweisung

scanf("%3s%3s%3s", s1, s2, s3);

ausführen und als Antwort September eingeben, wird Sep s1 zugewiesen, tem s2 und ber s3.

Beachten Sie, dass bei Verwendung der Funktion scanf(), ebenso wie bei der Funktion gets(), die Gefahr besteht, über das Ende des Arrays hinauszuschreiben.

Was passiert, wenn Sie weniger oder mehr Strings eingeben, als die Funktion scanf() erwartet? Wenn Sie weniger Strings eingeben, wartet scanf() auf die fehlenden Strings und hält das Programm an, bis diese Strings eingegeben wurden. Wenn Sie zum Beispiel als Antwort auf die Anweisung

scanf("%s%s%s", s1, s2, s3);

Januar Februar eingegeben hätten, hätte das Programm auf den dritten String, der im scanf()-Formatstring spezifiziert wurde, gewartet. Haben Sie mehr Strings als erforderlich eingegeben, bleiben die nicht übernommenen Strings im Tastaturpuffer stehen. Sie werden dann von der nachfolgenden scanf()-Funktion oder von anderen Eingabeanweisungen eingelesen. Wenn Sie zum Beispiel als Antwort auf die Anweisung

scanf("%s%s", s1, s2);
scanf("%s", s3);

Januar Februar März eingegeben hätten, würde im ersten Aufruf von scanf() Januar dem String s1 und Februar dem String s2 zugewiesen. März wird automatisch übertragen und im zweiten Aufruf von scanf() s3 zugewiesen.

Die scanf()-Funktion hat als Rückgabewert einen Integer, der die Anzahl der erfolgreich eingelesenen Elemente angibt. Der Rückgabewert wird oft ignoriert. Wenn Sie reinen Text einlesen, werden Sie scanf() normalerweise die Funktion fgets() vorziehen. scanf() sollte man am besten nur dann verwenden, wenn eine Kombination aus Text und numerischen Daten einzulesen ist. Listing 9.5 soll dies illustrieren. Denken Sie daran, dass Sie den Adressoperator (&) verwenden müssen, wenn Sie mit scanf() numerische Variablen einlesen wollen (siehe Tag 6).

Listing 9.5: Mit scanf() numerische Daten und Text einlesen.

1:  /* Beispiel für scanf(). */
2:
3: #include <stdio.h>
4:
5: char nname[81], vname[81];
6: int count, id_num;
7:
8: int main(void)
9: {
10: /* Aufforderung an den Anwender. */
11:
12: puts("Geben Sie, durch Leerzeichen getrennt, Nachnamen, Vornamen");
13: puts("und Kennnummer ein. Dann die Eingabetaste drücken.");
14:
15: /* Einlesen der drei Elemente. */
16:
17: count = scanf("%s%s%d", nname, vname, &id_num);
18:
19: /* Daten ausgeben. */
20:
21: printf("%d Elemente wurden eingegeben: %s %s %d \n",
count, vname, nname, id_num);
22:
23: return 0;
24: }

Geben Sie, durch Leerzeichen getrennt, Nachnamen, Vornamen
und Kennnummer ein. Dann die Eingabetaste drücken.
Meier Johann 12345
3 Elemente wurden eingegeben: Johann Meier 12345

Wie Sie bereits wissen, muss man scanf()als Argumente Adressen übergeben. In Listing 9.5 sind nname und vname Zeiger (das heißt Adressen), so dass sie den Adressoperator nicht benötigen. Im Gegensatz dazu ist id_num ein regulärer Variablenname, so dass hier das & bei der Übergabe an scanf() erforderlich ist (Zeile 17).

Manche Programmierer sind der Meinung, dass das Einlesen von Daten mit scanf() fehleranfällig sei. Sie ziehen es vor, alle Daten - sowohl numerische Daten als auch Text - mit fgets() einzulesen und im Programm die Zahlenwerte herauszufiltern und in numerische Variablen zu konvertieren. Solche Techniken gehen weit über den Rahmen dieses Buches hinaus, sind jedoch eine gute Programmierübung. Sie benötigen dafür allerdings die Funktionen zur Stringmanipulation, die am Tag 17, »Die Bibliothek der C-Funktionen«, behandelt werden.

Zusammenfassung

In der heutigen Lektion haben wir uns ausführlich mit dem Datentyp char beschäftigt. Ein Einsatzgebiet für char-Variablen ist das Speichern einzelner Zeichen. Sie haben gelernt, dass Zeichen eigentlich als Zahlen gespeichert werden. Der ASCII-Code weist jedem Zeichen einen numerischen Code zu. Deshalb können Sie char-Variablen auch dazu nutzen, um kleine Integerwerte abzuspeichern. Variablen vom Typ char gibt es sowohl mit als auch ohne Vorzeichen.

Ein String ist eine Folge von Zeichen, die mit einem Nullzeichen abgeschlossen wird. Strings können für Textdaten verwendet werden. C speichert Strings in Arrays vom Typ char. Um einen String der Länge n zu speichern, benötigen Sie ein Array vom Typ char mit n+1 Elementen.

Sie können Ihre Programme mit Speicherzuweisungsfunktionen wie malloc() dynamischer machen. Die Funktion malloc() erlaubt es Ihnen, für Ihre Programme den benötigten Speicher zu reservieren. Ohne diese Funktionen müssten Sie raten, wie viel Speicherplatz Ihr Programm benötigt. Sicherheitshalber würden Sie diese Zahl sehr hoch ansetzen, so dass mehr Speicher als nötig reserviert wird.

Fragen und Antworten

Frage:
Was ist der Unterschied zwischen einem String und einem Zeichenarray?

Antwort:
Ein String wird als eine Folge von Zeichen definiert, die mit einem Nullzeichen endet. Ein Array ist eine Folge von Zeichen. Deshalb ist ein String ein Array von Zeichen, der mit einer Null abgeschlossen wird.

Wenn Sie ein Array vom Typ char definieren, ergibt sich der eigentliche Speicherbereich, der für das Array reserviert wurde, aus der angegebenen Größe und nicht der Größe minus 1. Sie sind auf diese Größe festgelegt und können keine größeren Strings in dem Array ablegen. Sehen Sie ein Beispiel:

char land[10]="Philippinen";  /* Falsch! String länger als Array. */
char land2[10]="Polen"; /* OK, aber verschwendet Speicher, da */
/* String kürzer ist als das Array. */

Wenn Sie jedoch eine Zeigervariable auf den Typ char definieren, gelten diese Beschränkungen nicht mehr. Die Variable ist lediglich ein Speicherplatz für den Zeiger. Die eigentlichen Strings sind anderswo im Speicher gespeichert (wo genau, braucht Sie nicht zu kümmern). Es gibt keine Längenbeschränkung und auch keinen verschwendeten Speicherplatz. Der eigentliche String ist ja woanders gespeichert. Ein Zeiger kann auf einen String beliebiger Länge zeigen.

Frage:
Warum deklariere ich zum Ablegen der Werte nicht einfach große Arrays, anstatt eine Funktion zur Speicherallokation wie malloc() zu verwenden?

Antwort:
Auch wenn es vielleicht leichter scheint, große Arrays zu deklarieren, ist zu bedenken, dass dies nicht gerade eine effektive Nutzung des Speicherplatzes darstellt. Kleine Programme wie die der heutigen Lektion lassen die Verwendung einer Funktion wie malloc() anstelle von Arrays unter Umständen unnötig komplex erscheinen. Aber mit zunehmendem Programmumfang werden Sie Speicher sicher nur nach Bedarf reservieren wollen. Darüber hinaus können Sie dynamisch reservierten Speicher, den Sie nicht mehr benötigen, wieder zurückgeben, indem Sie ihn freigeben. Der freigegebene Speicher kann dann von einer anderen Variablen oder einem Array in einem anderen Teil des Programms belegt werden. (Tag 18, »Vom Umgang mit dem Speicher«, behandelt die Freigabe von dynamisch reserviertem Speicher.)

Frage:
Was passiert, wenn Sie in einem Zeichenarray einen String ablegen, der länger als das Array ist?

Antwort:
Dies kann zu Fehlern führen, die nur sehr schwer aufzuspüren sind. Sie können das zwar in C machen, aber alles, was im Speicher direkt nach dem Zeichenarray steht, wird dann überschrieben. Wenn Sie Glück haben, wird der betreffende Speicherbereich nicht genutzt. Genauso gut können dort aber auch Daten liegen, die Ihr Programm benötigt. Was genau passiert, hängt davon ab, was Sie überschreiben. Oft passiert erst einmal eine ganze Weile gar nichts. Trotzdem sollten Sie nichts riskieren.

Frage:
Warum sollten Sie fgets() anstelle von gets() verwenden?

Antwort:
Die Funktion gets() bietet keine Möglichkeit, die Länge des Zeichenarrays, in das eingelesen wird, anzugeben. Bei fgets() können Sie dagegen die Länge angeben und so verhindern, dass über das Ende des Arrays hinausgeschrieben wird.

Workshop

Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, sowie Übungen, die Sie anregen sollen, das Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Die Lösungen zu den Fragen und den Übungen finden Sie in Anhang C.

Quiz

  1. Welchen Wertebereich umfasst der Standard-ASCII-Zeichensatz?
  2. Wie interpretiert der C-Compiler ein einfaches Zeichen in einfachen Anführungszeichen?
  3. Wie wird in C ein String definiert?
  4. Was ist ein literaler String?
  5. Um einen String von n Zeichen zu speichern, benötigen Sie ein Zeichenarray von n+1 Elementen. Wozu benötigen Sie das zusätzliche Element?
  6. Wie interpretiert der C-Compiler literale Strings?
  7. Bestimmen Sie mit Hilfe einer ASCII-Zeichensatz-Tabelle oder einem Programm die numerischen Werte für folgende Zeichen:
  8. a. a
  9. b. A
  10. c. 9
  11. d. das Leerzeichen
  12. Übersetzen Sie mit Hilfe einer ASCII-Zeichensatz-Tabelle oder einem Programm die folgenden ASCII-Werte in die entsprechenden Zeichen:
  13. a. 73
  14. b. 32
  15. c. 99
  16. d. 0
  17. e. 2
  18. Wie viele Byte Speicher werden für die folgenden Variablen reserviert? (Ein Zeichen soll ein Byte groß sein.)
  19. a. char *str1 = { "String 1" };
  20. b. char str2[] = { "String 2" };
  21. c. char string3;
  22. d. char str4[20] = { "Dies ist String 4" };
  23. e. char str5[20];
  24. Gegeben sei folgende Deklaration:
    char *string = "Ein String!";
  25. Welche Werte ergeben sich demnach für:
  26. a. string[0]
  27. b. *string
  28. c. string[11]
  29. d. string[33]
  30. e. *(string+10)
  31. f. string

Übungen

  1. Deklarieren Sie in einer Zeile eine char-Variable namens buchstabe und initialisieren Sie sie mit dem Zeichen $.
  2. Deklarieren Sie in einer Zeile ein Array vom Typ char und initialisieren Sie es mit »Zeiger machen Spass!«. Das Array soll gerade groß genug sein, um den String aufzunehmen.
  3. Reservieren Sie in einer Zeile Speicher für String »Zeiger machen Spass!«, aber verwenden Sie diesmal kein Array.
  4. Setzen Sie Code auf, der Speicher für einen String mit 80 Zeichen reserviert und dann einen String von der Tastatur einliest und im reservierten Speicher ablegt.
  5. Schreiben Sie eine Funktion, die ein Array von Zeichen in ein anderes Array kopiert. (Hinweis: Gehen Sie dabei wie in den Programmen aus Tag 8 vor.)
  6. Schreiben Sie eine Funktion, die zwei Strings übernimmt. Zählen Sie die Anzahl der Zeichen in jedem String und liefern Sie einen Zeiger auf den längeren String zurück.
  7. OHNE LÖSUNG: Schreiben Sie eine Funktion, die zwei Strings übernimmt. Verwenden Sie die malloc()-Funktion, um ausreichend Speicher für die Verkettung der beiden Strings zu reservieren. Liefern Sie einen Zeiger auf diesen neuen String zurück.
  8. Wenn Sie zum Beispiel »Hallo » und »Welt!« übergeben, soll die Funktion einen Zeiger auf »Hallo Welt!« zurückliefern. Den verketteten Wert als dritten String zu verwenden, ist dabei die geringste Schwierigkeit. (Möglicherweise können Sie Ihre Antworten zu den Übungen 5 und 6 verwenden.)
  9. FEHLERSUCHE: Ist der folgende Code korrekt?
    char ein_string[10] = "Dies ist ein String";
  10. FEHLERSUCHE: Ist der folgende Code korrekt?
    char *zitat[100] = { "Lächeln, bald ist Freitag!" };
  11. FEHLERSUCHE: Ist der folgende Code korrekt?
    char *string1;
    char *string2 = "Zweiter";
    string1 = string2;
  12. FEHLERSUCHE: Ist der folgende Code korrekt?
    char string1[];
    char string2[] = "Zweiter";
    string1 = string2;
  13. OHNE LÖSUNG: Schreiben Sie mit Hilfe der ASCII-Tabelle aus dem Anhang ein Programm, das einen Kasten auf dem Bildschirm ausgibt, wobei Sie das Minus-Zeichen, den vertikalen Strich und für die Ecken das Pluszeichen verwenden.

In der heutigen Lektion haben Sie gelernt, wie man mit malloc() dynamisch Speicher reserviert. Nachdem Sie auf diese Weise Speicher reserviert haben, sollten Sie ihn, wenn Sie ihn nicht mehr benötigen, dem Computersystem wieder zur Verfügung stellen. Dies geschieht, indem Sie ihn freigeben. Am Tag 18, »Vom Umgang mit dem Speicher«, wird die Freigabe von reserviertem Speicher behandelt.



vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbackKapitelanfangnächstes Kapitel


© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH