C-Strings

schiese

Erfahrenes Mitglied
Hallo,

ich gucke mir gerade die Strings in C++ an und habe da etwas über den C-String gelesen. Ein einfacher String (ein von Anführungszeichen begrenztes Literal) der Form
C++:
"Text";
ist vom Typ const char*, also ein Zeiger auf konstante Zeichen.
Wenn ich jetzt etwas wie
C++:
const char* text = "Text";
habe, zeigt der Zeiger auf die erste Stelle im Speicher, an der die Zeichenfolge beginnt. Der Ausdruck
C++:
std::cout << &text << "\n";
gibt mir auch wie erwartet eine Speicheradresse zurück.
Wenn ich den Zeiger allerdings weiterschalte
C++:
while (*text) {
        std::cout << &text << "\n";
        text++;
}
erhalte ich viermal die gleiche Speicheradresse zurück und nicht vier verschiedene, wie ich erwartet habe.

Wieso gibt der weitergeschaltete Zeiger immer noch die Speicheradresse des Stringsanfangs zurück, aber den Buchstaben der jeweiligen Position aus?

Viele Grüße
schiese
 

schiese

Erfahrenes Mitglied
Hallo Technipion,

danke für deine Antwort.
Die Ausgabe von
C++:
&text
ist die Speicheradresse des Zeigers. Ich habe es jetzt mit printf gelöst um die Speicheradressen der einzelnen Zeichen des Strings zu erhalten
C++:
while (*text) {
        printf("Speicheradresse: %p\n", text);
        text++;
}

Ich habe gelesen, dass die einzelnen Zeichen eines Strings (inklusive des Null-Characters) im Arbeitsspeicher nacheinander abgespeichert werden. Das bekomme ich auch raus. Ist das immer so?

Gruß schiese
 

cwriter

Erfahrenes Mitglied
Ich habe es jetzt mit printf gelöst um die Speicheradressen der einzelnen Zeichen des Strings zu erhalten
Nicht nötig, das geht auch mit C++:
C++:
while(*text) {
    std::cout << "Speicheradresse: " << reinterpret_cast<const void*>(text) << std::endl;
    text++;
}
In C++ nimmt der operator<< einfach den Typ "char*" als String und nicht als Pointer, daher wird dann der (Sub)string und nicht die Adresse ausgegeben.

Ich habe gelesen, dass die einzelnen Zeichen eines Strings (inklusive des Null-Characters) im Arbeitsspeicher nacheinander abgespeichert werden. Das bekomme ich auch raus. Ist das immer so?
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
In translation phase 6, the multibyte character sequences specified by any sequence of adjacent character and wide string literal tokens are concatenated into a single multibyte character sequence. If any of the tokens are wide string literal tokens, the resulting multibyte character sequence is treated as a wide string literal; otherwise, it is treated as a character string literal. 5 In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. For character string literals, the array elements have type char, and are initialized with the individual bytes of the multibyte character sequence; for wide string literals, the array elements have type wchar_t, and are initialized with the sequence of wide characters corresponding to the multibyte character
(Seite 62)
Und der Array:
An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type
(Seite 35)

Also: Ja, der String Literal wird in einen Array kopiert (oder ist als solcher lesbar) und ein Array hat keine Lücken zwischen den Zeichen. Aber:
Es gibt wchar_t bzw. andere Speicherarten (Unicode bzw. als Speicherformat UTF-8/UTF-16/...).
D.h. z.B., dass wenn du ein L vor deinen Literal stellst, also
C++:
auto x = L"Hallo";
//decltype(x) == "const wchar_t*"
Wenn du dann auf "const char*" castest und dann iterierst, würdest du zwischen den einzelnen Zeichen 0-Werte haben. (wchar_t ist meistens 16bit).
Übrigens ein weiterer Grund, warum Casts in C++ sehr ungern gesehen werden. Daher auch der reinterpret_cast im Beispiel oben: Der erlaubt fast alles; es ist dann aber auch dein Problem, wenn nichts geht. Die C-style-casts sind sehr freigiebiger. In C++ nutzt man stattdessen Operator Overloading, um nicht-passende Typen passend zu machen. In C ging das nicht, deshalb sind dort Casts verbreiteter (was nicht passt, wird passend gemacht - man sieht die Probleme halt später).


Gruss
cwriter
 

ComFreek

Mod | @comfreek
Moderator
Es gibt wchar_t bzw. andere Speicherarten (Unicode bzw. als Speicherformat UTF-8/UTF-16/...).
Insbesondere sollte man sich früh von der Vorstellung lösen, dass "ein Zeichen == char" oder "ein Zeichen == 8 Bit" o. Ä.
Oder dass zwei Strings nur dann gleich sind, wenn ihre Bitinhalte gleich sind. Stichwort Unicode Normalization. Es gibt in Unicode z. B. verschiedene ein "ä" darzustellen. Einmal als "ä" selbst und ein anderes Mal als "a" + "zwei Punkte oben".
Man sollte auch immer besser nachdenken, bevor man über einen String Zeichen-für-Zeichen mit for iteriert. Das geht sehr schnell kaputt, wenn du nicht gerade nur ASCII erlaubst.
 

cwriter

Erfahrenes Mitglied
Insbesondere sollte man sich früh von der Vorstellung lösen, dass "ein Zeichen == char" oder "ein Zeichen == 8 Bit" o. Ä.
Alles völlig korrekt, allerdings muss man hier relativieren:
Gerätetreiber (bzw. Identifierstrings/Commandstrings darin), einfache Programme (für den Eigengebrauch), Microcontroller bzw. alles, was in ASCII sinnvoll machbar ist, sollte man in ASCII machen, wenn man in C/C++ programmiert. Mit etwas Phantasie auch in Wide Strings (vor allen Windows mit UCS-2), man verschenkt aber schon grosszügig Performance/Speicher.
Für alles, was ein Nutzer sehen soll: Unicode-Library verwenden. UTF-8 ist vor allem im Web sehr beliebt, und ich würde auch zu diesem Format raten: Standard-ASCII-Kompatibel (nicht extended ASCII), und man zahlt (fast) nur für spezielle Zeichen ausserhalb von ASCII mehr.

Gruss
cwriter