Forum: PC-Programmierung Grundlagen zu Datentypen


von Rocker (Gast)


Lesenswert?

Hallo,

ich habe große Schwierigkeiten mit den verschiedenen Datentypen in C++. 
Im Internet findet man Beispiele sowohl in Unicode als auch in 
Multibyte. Wenn ich nun mitten in einem Programm stecke und zufällig 
einen lehrreichen Beispielcode im "anderen" Format finde, geht der Ärger 
los...
Ich begegne auch immer wieder API-Funktionen, die zwar LPSTR 
zurückliefern, aber anscheinend die Zeichen in Byte-Form speichern, also 
kein Unicode. Das ist natürlich ungünstig, wenn man sich zu Beginn für 
Unicode entschieden hat.

Kennt jemand eine gute Informationsquelle für Anfänger im Internet, wo 
kurz und knackig die verschiedenen Datentypen und auch die Umwandlungen 
erklärt werden?

Quellen gibt es ja genug, aber ich habe so meine Probleme mit dem 
Verständnis...die folgende Erklärung habe ich Beispielsweise für die 
Funktion c_str() gefunden:

"The returned array points to an internal location with the required 
storage space for this sequence of characters plus its terminating 
null-character, but the values in this array should not be modified in 
the program and are only granted to remain unchanged until the next call 
to a non-constant member function of the string object."

Ok, also ein Null-Terminiertes Array mit dem Inhalt des String.
Ich frage mich jedoch, warum die Werte in dem Array nicht verändert 
werden sollten und was es nun genau mit dieser garantierten 
Unversehrtheit der Werte bis zum nächsten ominösen Funktionsaufruf auf 
sich hat?

Mit freundlichen Grüßen,
Rocker

von Sven P. (Gast)


Lesenswert?

Rocker schrieb:
> Ich begegne auch immer wieder API-Funktionen, die zwar LPSTR
Übe an gescheitem C++-Code und nicht an dem, was Microsoft darunter 
versteht. Deren Typenkatastrophe ist schlimm.

> "The returned array points to an internal location with the required
> storage space for this sequence of characters plus its terminating
> null-character, but the values in this array should not be modified in
> the program and are only granted to remain unchanged until the next call
> to a non-constant member function of the string object."

> Ok, also ein Null-Terminiertes Array mit dem Inhalt des String.
Genau, ein klassischer C-String.

> Ich frage mich jedoch, warum die Werte in dem Array nicht verändert
> werden sollten
Sie gehören dir nicht, ganz banal.

> und was es nun genau mit dieser garantierten
> Unversehrtheit der Werte bis zum nächsten ominösen Funktionsaufruf auf
> sich hat?
Das heißt: Wenn du dir mit c_str() den C-String liefern lässt und z.B. 
in einer char*-Variablen festhälst:
1
string EinCppString = ...;
2
char *s = EinCppString.c_str();
Dann ist der Inhalt des Arrays, auf das 's' nun zeigt (eben dein 
C-String) nur so lange korrekt (heißt hier: entspricht dem, was 
'EinCppString' auch enthält), so lange nicht neuerlich auf 
'EinCppString' herumgeackert wird.

Du führst ja eine Redundanz ein: Einerseits weiß 'EinCppString' 
natürlich über seinen Inhalt Bescheid, der in irgendeiner Form verwaltet 
wird. Mittels c_str() forderst du nun aber eine möglicherweise gänzlich 
andere Darstellung des Inhaltes an, d.h. schlimmstenfalls kopiert 
'EinCppString' seinen Inhalt, um ihn dir dann irgendwie anders 
aufbereitet zur Verfügung zu stellen.
Wenn du nun an der Kopie herumfummelst, kriegt das Original das nicht 
mit. Wenn du am Original herumfummelst, kriegt die Kopie das nicht mit 
(denn sie müsste ja z.B. wieder neu aufbereitet werden usw.) -> Deshalb 
nur gültig, bis zum nächsten Fummeln an 'EinCppString'.

von Matthias H. (experimentator)


Lesenswert?

> ich habe große Schwierigkeiten mit den verschiedenen Datentypen in C++.
> Im Internet findet man Beispiele sowohl in Unicode als auch in
> Multibyte. Wenn ich nun mitten in einem Programm stecke und zufällig
> einen lehrreichen Beispielcode im "anderen" Format finde, geht der Ärger
> los...
> Ich begegne auch immer wieder API-Funktionen, die zwar LPSTR
> zurückliefern, aber anscheinend die Zeichen in Byte-Form speichern, also
> kein Unicode. Das ist natürlich ungünstig, wenn man sich zu Beginn für
> Unicode entschieden hat.

Erst einmal ganz grundsätzlich:

- Die Programmiersprachen C und C++ kennen eigentlich keine Strings, 
sondern nur eine Möglichkeit, Zeichenkonstanten anzugeben, die 
automatisch durch ein Zeichen mit dem Wert 0 (ASCII: NUL) abgeschlossen 
werden. Alles andere ist in Bibliotheken implementiert, z. B. die 
C-Standardbibliothek (Funktionen wie strcpy()) oder die C++-STL (z. B. 
die Klasse std::string).

- Sogenannte C-Strings oder "nullterminierte Strings" sind entweder 
solche Zeichenkonstanten oder Variablen im gleichen Format bzw. Zeiger 
darauf.
Beispiel:
1
char* abc = "ABC";
Das definiert ein konstantes Character-Array von 4(!) Zeichen, nämlich 
A, B, C und einem 0-Byte, und einen Zeiger mit Namen abc darauf.

- Der Bezeichner LPSTR stammt aus einer Microsoft-Bibliothek und ist 
kein Standard.

- In C sind ursprünglich nur einfache Zeichen definiert, z. B. im 
ASCII-Standard. Der ASCII-Standard legt nur 7-Bit-Zeichen bis 127 fest, 
z. B., daß ein A als das Zeichen Nummer 65 codiert wird.

Wenn man Umlaute oder Sonderzeichen braucht, die nicht Bestandteil des 
zugrundeliegenden Zeichensatzes wie ASCII sind, dann muß man sich 
überlegen, wie man diese codiert. Da gibt es verschiedene Ansätze:

1. Codepages (z. B. ISO-8859-1): Man behält 1 Byte pro Zeichen bei, aber 
sagt, welche Bedeutung die Zeichen über 127 haben sollen, z. B. als 
welche Zahl ich ein Ü codiere.
Wenn irgendwo im Internet (E-Mails, Foren, ...) kaputte Umlaute 
auftauchen, dann ist der Grund meistens ein Problem mit Code pages.
Codepages sollte man für neuere Projekte nicht mehr verwenden, es sei 
denn, man hat z. B. ein Embedded-System, dessen Display sowieso nur 8 
Bits pro Zeichen erlaubt oder so.

2. Multi-Byte-Zeichensätze, immer noch mit Code pages. Das wurde z. B. 
für asiatische Sprachen benutzt, verwenden mehrere Bytes pro Zeichen und 
ist weitestgehend überholt.

3. Unicode: Dort hat man sich zusammengesetzt und sich überlegt, wie man 
die Zeichen praktisch sämtlicher Sprachen der Welt ohne Codepage-Chaos 
codieren kann. Herausgekommen ist eine Codierung, die in älteren 
Unicode-Versionen jedem Zeichen eine 16-Bit-Zahl und in neuen 
Unicode-Versionen eine 32-Bit-Zahl zuordnet.
Dabei sind die unteren 127 Zeichen identisch mit dem ASCII-Zeichensatz.

Da 16 oder 32 Bits sehr unhandlich sind (ein "char" hat je nach 
Plattform meist nur 8 Bit), hat man sich verschiedene Codierungen 
überlegt. Die gebräuchlichsten sind:

UTF-8: Dabei wird ein Zeichen variabel mit 1 bis 3 Bytes (in neueren 
Unicode-Versionen max. 4) dargestellt. Weil UTF-8 eine echte Obermenge 
von ASCII ist, kann man es wie ASCII bearbeiten, solange keine 
Sonderzeichen vorkommen (zum ersten Experimentieren vielleicht in 
Ordnung, aber ein fertiges Programm, das man herausgibt, sollte damit 
rechnen!).

UTF-16: Dabei wird ein Zeichen mit 2 Bytes (in neueren Unicode-Versionen 
2 oder 4) dargestellt.

UTF-32: Dort wird jedes Zeichen in 4 Bytes dargestellt.

Bei Unicode muß man also wissen, mit welcher Unicode-Variante man es zu 
tun hat. Am gebräuchlichsten und meiner Meinung nach meistens am 
handlichsten ist UTF-8, auch UTF-16 sieht man öfters, z. B. bei Java.

> Quellen gibt es ja genug, aber ich habe so meine Probleme mit dem
> Verständnis...die folgende Erklärung habe ich Beispielsweise für die
> Funktion c_str() gefunden:
>
> "The returned array points to an internal location with the required
> storage space for this sequence of characters plus its terminating
> null-character, but the values in this array should not be modified in
> the program and are only granted to remain unchanged until the next call
> to a non-constant member function of the string object."
>
> Ok, also ein Null-Terminiertes Array mit dem Inhalt des String.
> Ich frage mich jedoch, warum die Werte in dem Array nicht verändert
> werden sollten und was es nun genau mit dieser garantierten
> Unversehrtheit der Werte bis zum nächsten ominösen Funktionsaufruf auf
> sich hat?

Das bedeutet folgendes:

Wenn die Funktion c_str() (wahrscheinlich eher eine "Methode" einer 
Stringklasse wie std::string, oder?) aufgerufen wird, dann muß die 
Funktion einen Zeiger auf einen Speicherbereich liefern, in dem die 
Zeichen des Strings plus ein NUL-Zeichen stehen.
Dazu muß sie unter Umständen den String in einen neuen, größeren 
Speicherbereich kopieren, um Platz für das NUL-Zeichen zu schaffen.

Die Funktion kümmert sich selbst um die Verwaltung dieses 
Speicherbereichs. Darum darf der Aufrufer diesen Speicherbereich nur 
lesen, aber nicht freigeben (free()/delete) und nicht verändern, sonst 
ist das Verhalten undefiniert.
Nehmen wir zum Beispiel an, die Funktion würde einen Zeiger auf eine 
interne Datenstruktur des Strings zurückliefern und der Aufrufer würde 
diesen Speicherbereich ändern - dann würde sich auch der String ändern!
Es könnte auch zum Beispiel theoretisch sein, daß die Bibliothek als 
Optimierung einen Zeiger auf eine Konstante zurückliefert. Bei modernen 
Betriebssystemen mit Speicherschutz (wohl eher weniger bei 
Mikrocontrollern) könnte das dazu führen, daß das Betriebssystem das 
Programm beendet, anderenfalls wäre undefiniertes Verhalten die Folge.
Darum ist diese Methode als "const" deklariert, damit man das nicht 
versehentlich tut.

Und Du darfst den Rückgabewert dieser Funktion oder Methode nur so lange 
benutzen, bis die nächste Methode an diesem Stringobjekt aufgerufen 
wird, die nicht "const" ist. Wenn Du z. B. an den String etwas anfügst, 
dann ist der Rückgabewert des vorigen Aufrufs von c_str() ab diesem 
Zeitpunkt undefiniert und c_str() muß erneut aufgerufen werden, wenn der 
Wert das nächste mal gebraucht wird.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.