Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » FAQ C / C++ (ANSI-Standard) » Mit C Strings richtig umgehen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
21.07.2005, 21:43 Uhr
Pablo
Supertux
(Operator)


Ich dachte immer, ein Eintrag hier (FAQ ANSI C / C++) darüber wäre nicht notwändig, aber in letzter Zeit kamen verschiedenen Fragen darüber.

An dieser Stelle muss ich warnen, dass es sich hier (praktisch) auschließlich nur um (ANSI) C geht, das könnte man auch unter C++ anwenden, aber std::string wäre die beste Wahl unter C++.

Als erstes will ich ein wenig über Strings schreiben, denn die Handhabung von Strings in C ist nicht trivial und die muss man gut verstehen, bevor man mit Strings arbeitet.

char ist das Datentyp, das nur ein einzelnes Zeichen aufnehmen kann.


C++:
char c = 'a';
char d = 49; /* ASCII Code von '1' ist 49, also äquivalent zu d = '1'; */
char x = "a"; /* nicht erlaubt, das "a" kein Zeichen sondern eine Zeichenkette ist. */
char y = 'hallo'; /* auch nicht erlaubt, denn '' nur ein Zeichen haben kann. */



Ein String in C ist eine Zeichenkette, d.h. eine Aneinanderreihung von Characteren (von einzelnen Zeichen). Deswegen werden Strings durch char-Arrays oder char-Pointer dargestellt. Eigentlich kann man sagen, dass strings nichts anders sind als char-Arrays.

Da man unter C nicht immer zuverlässig die Anzahl der Elemente einer Arrays (beliebiges Typs) nachrechnen kann, hat man sich überlegt, wie man eindeutig kennzeichnen kann, wann ein String zu Ende ist. Die C Strings heißen nullterminierend, weil sie als letztes Zeichen das 0-Charakter enthalten. Das 0-Charakter ist aber nicht die '0' (ASCII Code 48) sondern die '\0' (ASCII Code 0). Mit diesem Zeichen beendet man jedes String in C, das ist ein Muss. Sollte '\0 ' fehlen, kann das sehr böse Folgen haben, weil die Routinen, die mit Strings arbeiten, nicht wissen können, wann sie aufhören sollen, und dadruch die häßlichen "Speicherzugriffsfehler" (Segmentation Faults) entstehen, die jeder am Anfang relativ häufig bekommt.

Unterschied zwischen char[] und char*
Ich habe bereits angedeutet, dass man Strings mit char-Arrays und char-Pointern darstellen kann, sie sind aber keine identische Datentypen und es gibt Unterschieden, auf die man auchten soll, wenn man damit arbeitet.
An sich ist nur ein char-Array das Datentyp, mit dem man Strings darstellen kann. Betrachten wir folgenden Code:


C++:
#include <stdio.h>

int main(void)
{
    char str[] = "Hallo"; /* Zur Erzeugung von Strings, siehen weiter unten */
    int i;

    for(i=0; i<5; ++i)
        printf("str[%d] = %c\n", i, str[i]);

    /* i ist an dieser Stelle 5 */
    if(str[i] == '\0')
        printf("str ist ein nullterminierendes String!\n");

    return 0;
}



Die Ausgabe des Programms ist:

Bash:

str[0] = H
str[1] = a
str[2] = l
str[3] = l
str[4] = o
str ist ein nullterminierendes String!



Wie wir hier sehen können, die Zeichenkette "Hallo" wird in diesem char-Array gespeichert und ganz am Ende wird das '\0'-Zeichen gespeichert.

Ein solches String kann immer verändert werden, sogar die Größe des Strings, doch man kann nur das Strings verkleinern, nicht vergrößern (hier geht es ums Strings und nicht um den Speicherplatz), somit ist hier auch möglich:


C++:
#include <stdio.h>

int main(void)
{
    char str[] = "Hallo"; /* Zur Erzeugung von Strings, siehen weiter unten */
    int i;

    for(i=0; i<5; ++i)
        printf("str[%d] = %c\n", i, str[i]);

    /* i ist an dieser Stelle 5 */
    if(str[i] == '\0')
        printf("str ist ein nullterminierendes String!\n");

    str[1] = 'e';

    printf("%s\n", str);

    str[2] = '\0'; /* Verkleinerung des Strings, aber nicht des Speichers */

    printf("%s\n", str);

    return 0;
}



Die Ausgabe des Programms ist:

Bash:

str[0] = H
str[1] = a
str[2] = l
str[3] = l
str[4] = o
str ist ein nullterminierendes String!
Hello
He




Jetzt schauen wir und dies hier an:


C++:
#include <stdio.h>

int main(void)
{
    char* str = "Hallo"; /* Zur Erzeugung von Strings, siehen weiter unten */
    int i;

    for(i=0; i<5; ++i)
        printf("str[%d] = %c\n", i, str[i]);

    /* i ist an dieser Stelle 5 */
    if(str[i] == '\0')
        printf("str ist ein nullterminierendes String!\n");

    str[1] = 'e';

    printf("%s\n", str);

    str[2] = '\0'; /* Verkleinerung des Strings, aber nicht des Speichers */

    printf("%s\n", str);

    return 0;
}



Die Ausgabe ist

Bash:

str[0] = H
str[1] = a
str[2] = l
str[3] = l
str[4] = o
str ist ein nullterminierendes String!
Segmentation fault



Was ist denn jetzt passiert? Es ist nämlich passiert, dass wir dieses String anders erzeugt haben und auf das String nur über einen Zeiger zugreifen können, das Strings jedoch aber konstant ist und deshalb nicht veränderbar ist.

Dieses Verhalten zeigt ein großer Unterschied zwischen char[] und char*. Wenn man einem char* hat, dann bedeutet es nur, dass man auf das String nur durch einen Zeiger zugreifen kann, man weiß aber nicht, ob der Speicher konstant ist oder nicht.

Strings deklarieren

Es gibt verschiedene Methoden ein String zu deklarieren und erzeugen. Ich empfehle immer ein char-Array zu deklarieren, weil man dadurch den Inhalt des Strings verändern kann, aber wie bereits erwähnt, das String darf nicht länger sein als die Anzahl von im String verfügbaren Stellen (inklusive '\0').


C++:
char str1[] = "Unbekannte Länge";
char str2[15] = "Bekannte Länge";
char str3[100] = "Hallo";
char str4[2] = "Hallo"; /* das erzeugt einen Compiler-Fehler, zumindest beim GNU GCC */



Bei der ersten Methode ist die Länge nicht bekannt, aber die Anzahl der Stellen am Array wird vom Compiler berechnet.
Bei der zweiten Methode ist die Länge bekannt.
Bei der dritten Methode macht man mehr oder wenig eine Abschätzung, d.h. man kann höchstens ein String Speichern, das höchstens 99 Zeichen lang ist.
Bei der vierten Methode ensteht ein Compiler Fehler, ist doch klar warum.

Die Zuweisung eines Arrays mit einem konstanten String kann nur während der Deklaration der Variablen, wie oben geschrieben. Dies jedoch spuckt einen Compiler Fehler:


C++:
char str[10];
str = "Hallo";



Das liegt daran, dass "Hallo" einen Zeiger auf die Stelle liefert, wo das konstante String "Hallo" gespeichert ist.

Wenn man mit char* arbeitet, sollte man davor sicher sein, was man damit machen kann, ob der Speicher konstant ist oder nicht. Wenn man aber dynamisch und zur Laufzeit Strings erzeugen will, von denen wir erst zur Laufzeit ermitteln können, dann muss man praktsich nur mit char* und malloc arbeiten; der Speicher muss reserviert werden und wenn man ihn nicht mehr braucht wieder freigegeben werden. Dazu kann man hier lesen

String Funktionen

C bietet viele Funktionen, mit denen man Strings behandeln kann.
Warum kann man nicht Strings so vergleichen?


C++:
int gleich(const char* a, const char* b)
{
    return a==b;
}



Das liegt daran, dass a==b eine 1 liefert, wenn a und b auf die gleiche Speicherstellen zeigen, und nicht 1 wenn a und b das gleiche String speichern. Um zu überprüfen, ob 2 Strings gleich sind, muss man strcmp(3) benutzen.

Diese sind die Funktionen, die ANSI C für die String Handhabung bereitstellt:
Für all diese Funktionen muss man #include <string.h> haben!

memchr(3)
Zum Suchen eines Zeichens (nicht unbedingt Strings)

memcmp(3)
Zum Vergliechen (nicht unbedingt Strings)

memcpy(3)
Zum Kopieren von Zeichen (nicht unbedingt Strings)

memmove(3)
Zum Kopieren von Zeichen (nicht unbedingt Strings)

memset(3)
Zum Belegen eines Puffers mit einem Zeichen (nicht unbedingt Strings)

strcat(3)
Um Strings hintereinander anhängen

strncat(3)
Um Strings hintereinander anhängen

strchr(3)
Zum Suchen eines Zeichens in einem Strings

strrchr(3)
Zum Suchen eines Zeichens in einem Strings

strcoll(3)
ähnliche wie strcmp

strcpy(3)
Um Zeichenketten zu kopieren

strncpy(3)
Um Zeichenketten zu kopieren

strcspn(3)
Berechnet die Länge von Teilzeichenketten

strerror(3)
Liefert die Fehlerbeschreibung zu einer Fehlernummer (siehe errno)

strlen(3)
Liefert die Länge eines Strings

strcmp(3)
Um Strings zu vergleichen

strncmp(3)
Um Strings zu vergleichen

strpbrk(3)
Zum Suchen von Zeichenketten in anderen Zeichenketten

strspn(3)
Berechnet die Länge von Teilzeichenketten

strstr(3)
Zum Suchen von Zeichenketten

strtok(3)
Zur Trennung von Zeichenketten durch Terminalsymbole.

strxfrm(3)
Viel zu schwer in kürzen Worten zu fassen.

Achtung: All diese Funktionen erwarten, dass die Strings 0-terminierend sind.
--
A! Elbereth Gilthoniel!
silivren penna míriel
o menel aglar elenath,
Gilthoniel, A! Elbereth!

Dieser Post wurde am 30.08.2005 um 17:31 Uhr von Pablo editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ FAQ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: