Frage zu Datentypgrößen
-
Hallo zusammen.
Ich komme, verständnismäßig, mit der Größenangabe mancher Datentypen nicht so ganz klar.
Ich möchte den Wert einer wchar_t-Variablen in einen C-String konvertieren, weil mir die Funktion einer Bibliothek die ich benutze, keine andere Wahl lässt (wchar_t, weil ich das Programm später mit der WinAPI schreibe und der Textinhalt eines Edit-Window im UNICODE vorliegt).Folgendes führt - verständlicher Weise, zur Compiler-Warnung "warning C4244: '=': Konvertierung von 'const WCHAR' in 'char', möglicher Datenverlust":
LPCWSTR wText = TEXT("UPDATE mitarbeiter SET vname='Ünal' WHERE manr=1;"); char* pAscii = NULL; int length; length = lstrlen(wText); pAscii = (char*) malloc(length * sizeof(char)); for(int index=0; index < length; index++) pAscii[index] = wText[index]; //Fehler hier
Deshalb formuliere ich die angemeckerte Zeile folgendermaßen:
pAscii[index] = wText[index] & 0xFF;
1. Frage: free(pAscii) verursacht einen Fehler, weil vermutlich mehr Speicher gebraucht, als allokiert wird. Aber diese Anweisung weist der Adresse pAscii[index] doch nur ein Byte zu - oder irre ich da?. Der reservierte Speicher sollte doch reichen.
2. Frage: Warum funktioniert das Folgende auch ohne Fehler?
printf("%c", wText[index]);
Müsste doch auch zumindest eine Datenverlustwarunung gemeldet werden - oder nicht? Es werden doch zwei Byte pro Zeichen an const char* (ein Byte pro Zeichen) übergeben.
3. Frage: Warum liefert sizeof() in der nächsten Zeile den Wert 4?
printf("%i", sizeof(0xFF));
Behandelt der Compiler solche Konstanten immer als 4-Byte-Werte?
Danke Euch für's lesen.
Gruß
Kai
-
Oh Mann, dein Post ist ja besonders lecker.
1. Von der Windows-API abhängig und sollte direkt verschoben werden (auch wenn ich die
LPTutnichtweh
-Gülle einfach durchwchar_t*
ersetzen undwcslen
verwenden könnte).(char*)malloc
? Der Gallileo-Verlag muss in Geld schwimmen.
3. Es fehlt dir das Nullterminierungszeichen beim Reservieren des Speichers und später beim Setzen. Das hier funktioniert bei mir:
#include <stdlib.h> #include <string.h> #include <stdio.h> int main(void) { wchar_t*wText=L"UPDATE mitarbeiter SET vname='Ünal' WHERE manr=1;"; char*pAscii=NULL; size_t length; int index; length=wcslen(wText); pAscii=malloc(length+1); for(index=0;index<length;index++) pAscii[index]=wText[index]; pAscii[length]=0; printf("%s\n",pAscii); return 0; }
Ausgabe:
UPDATE mitarbeiter SET vname='�nal' WHERE manr=1;
Denkt dir bei der kodierten Zahl einfach ein Byte dazu, welches normalerweise ein Unicode-Zeichen vorgestellt hätte.
Funzt, kann aber auch zugegebenermaßen Glück sein.
Ach ja, noch der Rest:
_Bongo schrieb:
1. Frage: free(pAscii) verursacht einen Fehler, weil vermutlich mehr Speicher gebraucht, als allokiert wird. Aber diese Anweisung weist der Adresse pAscii[index] doch nur ein Byte zu - oder irre ich da?. Der reservierte Speicher sollte doch reichen.
Siehe oben.
_Bongo schrieb:
2. Frage: Warum funktioniert das Folgende auch ohne Fehler?
printf("%c", wText[index]);
Weil der sich da nur das erste Byte rauspoppelt und ausgibt. Das hier funzt ja auch:
printf("%c\n",('a'<<8)|'b');
Gibt dann 'b' raus. Ich weiß aber grad nicht, ob das was mit der Endianness zu tun hat. Kommt darauf an, ob nur blind auf das erste Byte geguckt wird oder ob eine ordentliche Typumwandlung gemacht wird.
_Bongo schrieb:
3. Frage: Warum liefert sizeof() in der nächsten Zeile den Wert 4?
printf("%i", sizeof(0xFF));
Behandelt der Compiler solche Konstanten immer als 4-Byte-Werte?
Wenn du kein Suffix angibst:
printf("%lu|%lu\n",sizeof(0xf),sizeof(0xfULL));
ULL
macht aus der Konstanten einenunsigned long long
. Der ist bei mir 8 Byte groß. Deswegen gibt die Zeile bei mir auch4|8
raus.
-
_Bongo schrieb:
LPCWSTR wText = TEXT("UPDATE mitarbeiter SET vname='Ünal' WHERE manr=1;");
Aufpassen, Queries selber zusammenzubauen macht dich auf SQL_injection anfällig.
length = lstrlen(wText); pAscii = (char*) malloc(length * sizeof(char));
(malloc zu casten ist hier nicht gerne gesehen)
(sizeof char ist immer gleich 1)
Der String hat kein Platz das abschliessende Null-Byte.pAscii = (char*) malloc(length + 1);[code] [code="C"] for(int index=0; index < length; index++) pAscii[index] = wText[index]; //Fehler hier
pAscii[index] = wText[index] & 0xFF;
Dir ist klar, wieso LPCWSTR mehr als nur ein Byte pro Zeichen speichert? Ein char kann nur 256 Werte annehmen, und Sonderzeichen wie "äöüÄÖÜ" sind da nicht drin. Du versuchst, ein 16-Bit-Wert in einen 8-Bit-Wert umzuwandeln, indem du einfach die vorderen Bytes abschneidest. Das führt zu Werteverlust, ist dir das bewusst?
Wenn deine Bibliothek char* erwartet, kann das auch daran liegen, dass sie UTF-8 und kein ASCII erwartet. UTF-8 ist eine Zeichenkodierung, bei der einzelne Codepoints (≈"Zeichen") in mehreren Bytes abgespeichert werden können, aber nicht immer alle. Das geht mit deinem Ansatz nicht, dafür würde ich lieber auf Bibliotheken umsteigen, die das können, aber dazu musst du uns mehr Info über die Bibliothek usw. geben.
1. Frage: free(pAscii) verursacht einen Fehler, weil vermutlich mehr Speicher gebraucht, als allokiert wird. Aber diese Anweisung weist der Adresse pAscii[index] doch nur ein Byte zu - oder irre ich da?. Der reservierte Speicher sollte doch reichen.
Kann mit dem Nullbyte zusammenhängen oder mit allgemeinen Speicherfehlern. Da fehlen Informationen um das genau zu sagen.
2. Frage: Warum funktioniert das Folgende auch ohne Fehler?
printf("%c", wText[index]);
Das ist eine C-Eigenheit.
Any signed or unsigned char, short, enumerated type, or bit field is converted to either a signed or an unsigned int using integral promotion.
3. Frage: Warum liefert sizeof() in der nächsten Zeile den Wert 4?
printf("%i", sizeof(0xFF));
Behandelt der Compiler solche Konstanten immer als 4-Byte-Werte?
In C ist fast alles ein int. 'c' ist ein int und 0xFF und 255 ist ein int (also üblicherweise 4-Byte).
Danke Euch für's lesen.
Gruß
Kai[/quote]
-
Bingo_ schrieb:
In C ist fast alles ein int.
Quatsch.
-
_Bongo schrieb:
Ich möchte den Wert einer wchar_t-Variablen in einen C-String konvertieren, weil mir die Funktion einer Bibliothek die ich benutze, keine andere Wahl lässt (wchar_t, weil ich das Programm später mit der WinAPI schreibe und der Textinhalt eines Edit-Window im UNICODE vorliegt)
Der Textinhalt des Edit-Windows muss nicht in UNICODE vorliegen, Du kannst genau so gut WinAPI - Anwendungen unter Benutzung eines Multibytecharakter-Zeichensatzes (ANSI) schreiben, Du musst das dann nur in Deiner IDE entsprechend einstellen.
Für die von Dir gewünschte Konvertierung stellt die WinAPI eine Funktion zur Verfügung:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130(v=vs.85).aspx
-
Statt selbst Strings zu konvertieren, bietet die (Windows)-C-Runtime in stdlib.h auch:
size_t wcstombs (char* dest, const wchar_t* src, size_t max);
Hierbei wird die aktuelle Codepage benutzt, du musst nur noch einen ausreichend großen Ziel-Puffer bereitstellen und auf Null-Terminierung achten.
wcstombs ist also eine (Windows)CRT Funktion und keine WinAPI Funktion im eigentlichen Sinn.
-
_Bongo schrieb:
2. Frage: Warum funktioniert das Folgende auch ohne Fehler?
printf("%c", wText[index]);
"Funktioniert" im Kontext eines Anfängern ist subjektiv, objektiv ist dein "funktionierender" Versuch nämlich UB, weil wchar_t als Argument bei printf "%lc" verlangt und nicht "%c".
-
Ich danke Euch für die vielen Antworten. Das Dunkel lichtet sich ein wenig. Jetzt versuche ich allerdings noch, diese Daten in eine MySQL-Datenbank zu übertragen. Eigentlich funktioniert das auch ohne größere Beanstandungen - mit Ausnahme von Umlauten. Diese werden, verständlicher Weise, falsch in die Tabelle eingetragen. Die API-Funktion dazu lautet:
int mysql_real_query(MYSQL *mysql, const char *q, unsigned long length);
Ich habe es schon mit WideCharToMultiByte versucht und das Resultat testweise auf dem Bildschirm ausgegeben - Ergebnis war aber unverändert. Ein anderer Versuch sah so aus, dass ich den Zeichensatz der Datenbank-Tabelle auf den meines Systems umgestellt habe. Auch das hatte keinerlei Änderungen hervorgebracht. Allerdings wurde der Standard-MySQL-Client, soweit ich das gelesen habe, mit der selben API geschrieben - bei dem klappt es.
Ich habe jetzt schon so allerhand versucht. Das Ergebnis ist immer dasselbe.
Weiss da einer Rat?
*****************
Fragen zu einigen der letzten Antworten:
Von der Windows-API abhängig und sollte direkt verschoben werden (auch wenn
ich die LPTutnichtweh-Gülle einfach durch wchar_t ersetzen und wcslen verwenden könnte).*
:
Wo ist der Unterschied zwischen LPCWSTR und wchar_t*, wenn LPCWSTR lediglich ein typedef von wchar_t* ist. Dann ist es doch egal, was ich schreibe - wchar_t* oder seinen typedef.(char)malloc? Der Gallileo-Verlag muss in Geld schwimmen.*
und
(malloc zu casten ist hier nicht gerne gesehen)
(sizeof char ist immer gleich 1)
:
Dann ist mein Compiler (MSVC++2008) ein geächteter Geldverschwender. Die Anweisung char* pAscii = malloc(length+1); führt zur Fehlermeldung, dass void* nicht nach char* konvertiert werden kann.Es fehlt dir das Nullterminierungszeichen beim Reservieren des Speichers und später beim Setzen.
:
Das fehlt nicht. Hatte es nur versehentlich nicht mit kopiert. Trotzdem hast du mich, durch deinen Beispielcode, auf meinen Fehler aufmerksam gemacht. Ich reservierte nur length Stellen und nicht length+1. Ich glaube, dass nennt man Fencepost-Error.Dir ist klar, wieso LPCWSTR mehr als nur ein Byte pro Zeichen speichert? Ein char kann nur 256 Werte annehmen, und Sonderzeichen wie "äöüÄÖÜ" sind da nicht drin. Du versuchst, ein 16-Bit-Wert in einen 8-Bit-Wert umzuwandeln, indem du einfach die vorderen Bytes abschneidest. Das führt zu Werteverlust, ist dir das bewusst?
:
Die Buchstaben und Ziffern in den Zeichensätzen, decken sich aber. Die Konvertierung funktioniert, ohne unerwünschten Werteverlust. Dadurch, dass ich mittels "&0xFF" absichtlich ein Byte rausmaskiere bin ich mir bewusst, was ich da tue.
Ich verstehe nur nicht, warum in den 256 Zeichen keine Umlaute stecken sollen. Was ist mit dem den ASCII-Codes:
ö 148
Ö 153
Ü 154
ä 132
ü 129
Ä 142
-
_Bongo schrieb:
Dann ist mein Compiler (MSVC++2008) ein geächteter Geldverschwender. Die Anweisung char* pAscii = malloc(length+1); führt zur Fehlermeldung, dass void* nicht nach char* konvertiert werden kann.
Was daran liegen dürfte, dass du dem Compiler nicht mitgeteilt hast (durch die Dateiendung oder entsprechende Compilereinstellungen), dass dein Quelltext in C und nicht in C++ geschrieben ist. Und weil Visual C++ lieber C++ als C macht, bekommst du dann eben einen Fehler, der so nur in C++ zutrifft. Wäre das allerdings C++, würde man gar nicht erst malloc verwenden.
-
Warum ist denn das casten des malloc-Resultats auf char* so verpönt. Ist es wegen des unnötigen Aufwands, ein Byte in ein Byte umzuwandeln?.
-
Der Cast geschieht sowieso automatisch, mit dem Extra-Cast zeigt man halt dass man einen schlechten C-Lehrer hatte.
-
Ich denke, dass mein eigentliches Problem, im zweiten Parameter von mysql_real_query() liegt. Der Code für das 'Ü', in der Codepage cp850, die auf meinem XP Anwendung findet, lautet 0x9A (154). Die kann ich ja nicht mit dem Wertebereich von char darstellen.
-
Doch, das geht. Ox9A kann entweder negativ sein, falls char, signed ist, bzw. positiv, falls char unsigned ist.
char, signed char und unsigned char sind verschiedene Typen, da char entweder signed oder unsigned sein kann.
-
Super - funktioniert tatsächlich. Habe das mit dem Code 0x9A zwar schon einmal ausprobiert, aber irgendwas habe ich da wohl verkehrt gemacht. Ich danke dir für die Lösung.
Allerdings verstehe ich noch nicht so ganz, wie das abläuft. Wenn ich die Zuweisung pAscii[30] = (char) 0x9A; definiere, dann enthält diese Adresse den Wert 102, statt, wie ich gemeint habe, 154.
Genauso wenig verstehe ich, warum man bei signed char einen Wertebereich von -128 bis 127 hat. Wenn doch das höchste Bit eines Byte für das Vorzeichen gebraucht wird, dann hat man doch auf jeder Seite (+ und -) 7 Bits, um einen Wert darzustellen - also 2↑7 - 1. Somit komme ich auf 127 auf beiden Seiten.
-
_Bongo schrieb:
Genauso wenig verstehe ich, warum man bei signed char einen Wertebereich von -128 bis 127 hat. Wenn doch das höchste Bit eines Byte für das Vorzeichen gebraucht wird, dann hat man doch auf jeder Seite (+ und -) 7 Bits, um einen Wert darzustellen - also 2↑7 - 1. Somit komme ich auf 127 auf beiden Seiten.
Das wäre das Einerkomplement. Da hast du dann aber zweimal die Null: +0 und -0.
Aber so rechnen die wenigsten Prozessoren.Heute wird meist das Zweierkomplement benutzt.
^Wer kennt Lagerfeuergexhichten mit Einerkomplement?^
-
_Bongo schrieb:
Allerdings verstehe ich noch nicht so ganz, wie das abläuft. Wenn ich die Zuweisung pAscii[30] = (char) 0x9A; definiere, dann enthält diese Adresse den Wert 102, statt, wie ich gemeint habe, 154.
Wie stellst du das fest?
154 und -102 (beachte das Vorzeichen) haben dassselbe Bitmuster (bei Zweierkomplement und 8-Bit)
-
DirkB schrieb:
Das wäre das Einerkomplement. Da hast du dann aber zweimal die Null: +0 und -0. Aber so rechnen die wenigsten Prozessoren.
D.h., statt -0 zu verwenden, wird die negative Seite, um einen Wert erweitert?
DirkB schrieb:
Wie stellst du das fest? 154 und -102 (beachte das Vorzeichen) haben dassselbe Bitmuster (bei Zweierkomplement und 8-Bit)
Habe eine Kontrollvariable definiert und über den Debugger verfolgt (hast Recht mit -102. Hatte mich vertan).
Wenn ich das Komplement zu 154 (von Hand) bilde, dann komme ich aber auf 101. Wo vergesse ich denn da wieder ein Bit?
-
_Bongo schrieb:
Wenn ich das Komplement zu 154 (von Hand) bilde, dann komme ich aber auf 101.
Das wäre wieder das Einerkomplement.
-
JWolfFan schrieb:
Der Cast geschieht sowieso automatisch, mit dem Extra-Cast zeigt man halt dass man einen schlechten C-Lehrer hatte.
Wenn der Cast sowieso automatisch geschieht, dann frage ich mich, warum überhaupt casten - ausgenommen die Antwort "weil ansonsten der Compiler meckert". Das hört sich so an, als ob der Compiler sowieso implizit castet - was er ja nicht tut. Oder bedeutet kein Cast einfach nur, dass void* dann eben ein Zeiger auf ein Byte (char) ist?
-
_Bongo schrieb:
JWolfFan schrieb:
Der Cast geschieht sowieso automatisch, mit dem Extra-Cast zeigt man halt dass man einen schlechten C-Lehrer hatte.
Wenn der Cast sowieso automatisch geschieht, dann frage ich mich, warum überhaupt casten - ausgenommen die Antwort "weil ansonsten der Compiler meckert". Das hört sich so an, als ob der Compiler sowieso implizit castet - was er ja nicht tut. Oder bedeutet kein Cast einfach nur, dass void* dann eben ein Zeiger auf ein Byte (char) ist?
Der (C-) Compiler meckert nicht, wenn man denn Cast weglässt. Das funktioniert automatisch. Der Cast ist schlicht und ergreifend überflüssig.