Ich habe jetzt noch ein kleineres Problem. Mein C++ Compiler klopft mir
auf die Finger wenn ich zum Beispiel die sprintf Funktion nutzen möchte
und als String zum Beispiel den Datentyp uint8_t definiert habe.
Kann man das auch irgendwie definieren, dass der Compiler merkt, dass es
der gleiche Datentyp ist?
Bzw. was benutzt ihr allgemein so für Datentypen wenn ihr in C++
programmiert?
Ich wollte gern bei den vorgeschlagenen Datentypen im Tutorial bleiben.
Also uint8_t, uint16_t...
Ich denke nicht, dass es sinnvoll ist, ALLE Variablen mit den uint..
Typen zu definieren.
Ein String besteht nun mal aus Zeichen, also char (oder wchar etc) und
sollte damit definiert werden.
Nur für Variablen, bei denen die genaue Größe wichtig ist, und mit denen
gerechnet wird (-> deshalb integer), bzw. Bitmanipulationen gemacht
werden, sind die uint.. Typen sinnvoll.
Also gib Cäsar was des Cäsars ist, und definiere Deine Strings mit char.
Eine Ursache könnte sein, daß print von signed char ausgeht - dann
paßt uint8_t natürlich nicht. (C++ unterscheidet da sehr genau.)
Versuchs mal mit int8_t.
Interessant könnte auch noch sein, wie unint8_t definiert ist. Poste mal
die Definition von uint8_t und den Prototyp von sprintf (wie sie im
Headerfile deklariert ist), wenn int8_t nicht hilft.
Ich finde das ein wenig inkonsistent. Deshalb bin ich auch Klaus
Falser's Meinung diesbezüglich:
Klaus Falser wrote:
> Ich denke nicht, dass es sinnvoll ist, ALLE Variablen mit den uint..> Typen zu definieren.> Ein String besteht nun mal aus Zeichen, also char (oder wchar etc) und> sollte damit definiert werden.
Datentyp bei Zeichen also: char
Datentyp bei (normalen) 8-Bit werten: uint8_t
Datentyp bei (normalen) vorzeichenbehafteten 8-Bit werten: int8_t
Uhu Uhuhu wrote:
> Sehe ich auch so... Nur ist es doch sicherlich lehrreich,> herauszufinden, warum der Compiler meckert, oder?
Weil (per definitionem, auch in C!) "char" != "signed char" !=
"unsigned char" ist. Auch wenn char stets entweder wie signed
char oder wie unsigned char implementiert ist, ist es zu
keinem der beiden typkompatibel. Alle drei muss man also einfach
klar auseinander halten.
Während das in C eine Warnung ist, ist es halt in C++ ein Fehler.
Die Umwandlung von char nach uint8_t passiert in der Regel exakt
einmal, an der Peripherie. Wenn ich also eine uart_putc()-
Funktion für stdio schreibe, so bekommt die ein char rein (weil
stdio logischerweise durchweg mit char arbeitet, es sind ja
darstellbare Zeichen), und bevor ich dann das char an das
USART-Register geben kann, muss ich es halt nach uint8_t casten.
Reinzu genau umgekehrt.
Ist nicht ein char entweder ein signed char oder ein unsigned char?
Welcher Fall zutrifft ist implementierungsabhängig, aber jeweils 2 Typen
sind meiner Meinung nach identisch.
Identisch sind sie nicht. Wenn du z.B. vergleichen oder gar sortieren
willst und Zeichen mit gesetztem oberen Bit hast, spielt das eine sehr
große Rolle und C++ packt die Information darüber, um welchen char-Typ
es sich handelt, in die Funktionssignatur.
Compiler haben üblicherweise eine Defaulteinstellung für den char-Typ.
Man kann ihn aber auch per Compileroption einstellen.
Na da habe ich ja eine schöne Diskussion ausgelöst. ;-)
Also zunächst hatte ich mich an das Tutorial hier auf der Seite
gehalten. Da ist das Beispiel bei: "Die Nutzung von printf"
...
uint8_t s[20];
sprintf( s, "Zählerstand: %d", value );
uart_puts( s );
...
Das müsste mit dem C-Compiler funktionieren, mit dem C++ Compiler aber
nicht.
Ich glaube das Tutorial ist hier an der stelle etwas großzügig, oder?
Das signed char und unsigned char nicht das gleiche ist, ist mir schon
klar, aber wo ist dann der Unterschied zu char?
Für char nimmt der Compiler entweder den Default-Typ, oder den per
Option eingestellten.
Was der Default bei gcc ist, weiß ich nicht - da mußt du eben mal
nachlesen... Bei VC z.B. ist es unsigned char.
C ist bei solchen Kleinigkeiten nicht so pingelig und castet implizit.
> Ist nicht ein char entweder ein signed char oder ein unsigned char?
Nein.
> Welcher Fall zutrifft ist implementierungsabhängig, aber jeweils 2> Typen sind meiner Meinung nach identisch.
Die interne Wert-Repräsentation ist mit einer von beiden identisch (mit
welcher, ist tatäschlich implementierungsabhängig, und beide Varianten
sind verbreitet), aber dennoch ist char immer ein eigener Typ.
> Was der Default bei gcc ist, weiß ich nicht
Das ist wiederum von der Zielplattform abhängig. Bei avr ist es meines
Wissens vorzeichenbehaftet. Da man aber für Zeichen (und nur dafür) eh
immer char verwendet, spielt das keine Rolle. Wie Jörg schon
geschrieben hat, castet man das dort, wo es zur Hardware geht, einmal
um. Ob char ein Vorzeichen hat oder nicht, braucht man überhaupt nicht
zu wissen.
Mit anderen Worten: eine Applikation, die zum C-Standard konform ist,
darf ihr Verhalten nicht ändern, egal ob "char" signed oder unsigned
implementiert ist. Bei C ist das eine Warnung, bei C++ ist es ein
Fehler, wenn man beides miteinander vermischt.
Das führt aber in eine üble Falle:
Angenommen, Modul A ist mit der Option 'char is signed' compiliert und
Modul B mit 'char is unsigned' und in beiden Modulen werden Zeichen
verglichen.
Der Linker merkt nicht, daß die in den Modulen definierten Funktionen
von völlig verschiedenen Voraussetzungen ausgehen und nagelt daraus ein
Programm zusammen...
Das widerspricht der Grundannahme 'Typsicherheit' von C++ eklatant und
deckt sich auch nicht mit den täglichen Erfahrungen, die man so mit
Linkerproblemen macht.
Der Sache mussen wir auf den Grund gehen.
Rolf, kannst du deine Ausführungen belegen?
> Angenommen, Modul A ist mit der Option 'char is signed' compiliert und> Modul B mit 'char is unsigned' und in beiden Modulen werden Zeichen> verglichen.
Deshalb belässt man das auch auf Default und stellt es nicht per
Kommandozeile um.
> Das widerspricht der Grundannahme 'Typsicherheit' von C++ eklatant und> deckt sich auch nicht mit den täglichen Erfahrungen, die man so mit> Linkerproblemen macht.
Es gibt noch andere Compiler-/Linkeroptionen, die auf das ABI Einfluß
haben. Wenn man die anfaßt, ist man eben selbst dafür verantwortlich,
daß das Programm und alle verwendeten Bibliotheken mit denselben
Einstellungen übersetzt sind bzw. daß man nichts davon übergreifend
verwendet. Wenn man ein Programm mit -mint8 (int-Größe auf 8 Bit
verstellen) compiliert, ist ja auch klar, daß man das nicht einfach mit
anderem Code zusammenlinken kann, bei dem die Option nicht gesetzt ist.
> Rolf, kannst du deine Ausführungen belegen?
Welche meinst du? Ich habe nicht behauptet, man könne Code, der
verschiedene Einstellungen verwendet, beliebig mischen, sondern nur, daß
es mir als Programmierer egal sein kann, ob char signed oder unsigned
ist. Daß es programmweit einheitlich sein muß, ist eigentlich klar.
In meinem Nachschlagewerk: C/C++ Die Referenz, steht zu Datentypen:
unsigned char 0 bis 255 1Byte
signed char -128 bis 127 1Byte
Das ist ja denke ich mal auch allen klar und verständlich.
Und was ist jetzt char? Rolf kannst du uns das bitte nochmal genau
erklären, wenn das keiner der beiden sein sollte?
@Lars:
Grundsätzlich (default-Einstellung, ANSI-Standard) gilt: Wenn die
VOrzeichenangabe bei einem Datentyp weggelassen wird, ist der Typ
vorzeichenbehaftet. Der Wertebereich von char entspricht also dem von
signed char. Bei einem int gilt entsprechendes. Man kann dem Compiler
aber optional mitteilen, dass er den Datentyp (in den mir bekannten
Fällen geht das afair aber nur für char) ohne Vorzeichenangabe als
unsigned behandeln soll. Das ist dann aber eine lokale,
benutzerspezifische Einstellung, die nicht mit dem Standard kompatibel
ist.
Wenn man char allerdings nur für Textzeichen (ASCII) verwendet (wie es
ja auch ursprünglich vorgesehen ist, der Datentyp heißt nicht umsonst
"Character"), ist es völlig wurscht, ob man die Einstellung abweichend
vom Default gemacht hat, weil die Einstellung ja für alle char im Code
gilt. Der "Missbrauch" von char als Rechenvariable ist auch auf
8-Bit-Systemen eigentlich überflüssig, wenn man die stdint.h einbindet
und die darin befindlichen Datentypen benutzt (und char eben nur für
ASCII-Zeichen verwendet).
@Rolf Magnus
Ich habs mit VC2005 mal näher unter die Lupe genommen:
char ist dort per Voreinstellung signed char.
Die Kommandozeilenoption /J ändert char in unsigned char.
Der Compiler erzeugt für char tatsächlich dieselbe Funktionssignatur,
gleichgültig, ob mit oder ohne /J compiliert.
Die Fehlermeldung, von der Lars berichtet - die er aber nicht gepostet
hat - kommt dann wohl davon, daß die gcc Runtimelib kein sprintf für den
Typ unsigned char enthält.
Johannes M. wrote:
> Wenn man char allerdings nur für Textzeichen (ASCII) verwendet (wie es> ja auch ursprünglich vorgesehen ist, der Datentyp heißt nicht umsonst> "Character"), ist es völlig wurscht, ob man die Einstellung abweichend> vom Default gemacht hat, weil die Einstellung ja für alle char im Code> gilt.
Das mag für den englischen Zeichensatz gelten - beim deutschen sind
etliche Zeichen mit gesetztem obersten Bit codiert und dann ist es schon
nicht mehr 'völlig wurscht', denn das Vergleichsergebnis für < und > ist
nicht dasselbe.
Johannes M. wrote:
> Grundsätzlich (default-Einstellung, ANSI-Standard) gilt: Wenn die> VOrzeichenangabe bei einem Datentyp weggelassen wird, ist der Typ> vorzeichenbehaftet.
Bei `int' ja, bei `char' nein. Die Vorzeichenhaftigkeit von `char'
ist (leider) von den Standards implementierungsabhängig gelassen
worden.
Daher gilt die Annahme (für ein portables Programm), dass `char',
`signed char' und `unsigned char' drei voneinander verschiedene, nicht
zuweisungskompatible Datentypen sind, auch wenn der ersten immer
tatsächlich als einer der beiden letzten implementiert ist.
Unser Waldvogel schrieb:
> Das mag für den englischen Zeichensatz gelten - beim deutschen sind> etliche Zeichen mit gesetztem obersten Bit codiert und dann ist es> schon nicht mehr 'völlig wurscht', denn das Vergleichsergebnis für <> und > ist nicht dasselbe.
Egal, der Vergleich auf kleiner oder größer ist vom Standard sowieso
nur für die Buchstaben a..z, A..Z und die Ziffern 0..9 jeweils
untereinander definiert. Alles andere kann man nicht auf kleiner oder
größer vergleichen. Damit ist es auch egal, ob char nun
vorzeichenbehaftet ist oder nicht.
> daß die gcc Runtimelib kein sprintf für den> Typ unsigned char enthält.Alle stdio-Operationen gemäß Standard arbeiten auf dem Typ `char'
und sind damit unverträglich mit sowohl `signed char' als auch
`unsigned char'.
Mir ist unklar, was es hier immer noch zu diskutieren gibt... Der
Standard ist doch so sonnenklar, alle drei sind als verschieden zu
behandeln, punkt.
@Lars Lochmann
> In meinem Nachschlagewerk: C/C++ Die Referenz, steht zu Datentypen:> unsigned char 0 bis 255 1Byte> signed char -128 bis 127 1Byte> Das ist ja denke ich mal auch allen klar und verständlich.
Allerdings nicht ganz allgemeingültig. Die angegebenen Bereiche sind
nach C-Norm nur Mindestwerte. Es wären auch größere Wertebereiche
erlaubt. In der Praxis ist es aber in den allermeisten Fällen korrekt.
> Und was ist jetzt char?
Es hat denselben Wertebereich und dieselbe Größe wie eines der beiden,
wobei es vom Compiler abhängt, welches gewählt wird. Es ist aber dennoch
nicht derselbe Typ.
> Rolf kannst du uns das bitte nochmal genau erklären, wenn das keiner> der beiden sein sollte?
Anderes Beispiel: Auf AVR ist int und short gleich groß. Sie haben die
gleiche Repräsentation und den gleichen Wertebereich. Dennoch sind es
zwei verschiedene Typen. Und so sind auch signed char, unsigned char und
char drei eigenständige Typen, obwohl char dieselbe Repräsentation hat
wie einer der beiden anderen. In C spielt das keine so große Rolle, aber
in C++ kann man z.B. Funktionen überladen. Da kannst du eine Funktion
für char, signed char und unsigned char überladen, und der Compiler
unterscheidet zwischen den dreien abhängig davon, welchen Typ man
übergibt. Folgendes Beispiel-Programm wird vom Compiler nicht
akzeptiert, weil er nicht weiß, ob er den Wert nun nach signed char oder
nach unsigned char konvertieren soll.
1
voidfunc(unsignedcharc){}
2
voidfunc(signedcharc){}
3
4
intmain()
5
{
6
charc='x';
7
func(c);
8
}
Bei den anderen Integertypen ist das nicht so. int und signed int sind
ein und derselbe Typ, daher ist folgender Code auch zulässig in C++:
1
voidfunc(unsignedinti){}
2
voidfunc(signedinti){}
3
4
intmain()
5
{
6
inti=42;
7
func(i);
8
}
@Johannes M.
> Grundsätzlich (default-Einstellung, ANSI-Standard) gilt: Wenn die> VOrzeichenangabe bei einem Datentyp weggelassen wird, ist der Typ> vorzeichenbehaftet.
Ja, für alle außer char.
> Der Wertebereich von char entspricht also dem von signed char.
Nicht zwangsweise.
@Uhu Uhuhu:
> beim deutschen sind etliche Zeichen mit gesetztem obersten Bit codiert> und dann ist es schon nicht mehr 'völlig wurscht', denn das> Vergleichsergebnis für < und > ist nicht dasselbe.
Es macht dann zwar einen Unterschied, aber im Prinzip sind eh beide
Ergebnisse falsch, weil die Umlaute und anderen Zeichen falsch
einsortiert werden. Zur Not schreibt man sich eine Funktion oder ein
Makro, wo der Vergleich gekapselt ist, und wo intern auf den gewünschten
Typ gecastet wrid.
Letztendlich vergleiche ich auf Ungleichheit meistens nur ganze Strings
(zum Sortieren), und da ist es die Aufgabe von strcmp, sich darum zu
kümmern.
> Auf jeden Fall ist es der Grund, warum ich signed char als Standardtyp> für char nicht mag.
Das Problem ist in dem Fall der Vergleich, nicht der char-Typ. Sobald
du statt mit ASCII z.B. mal mit EBCDIC arbeiten mußt, ist das Ergebnis
von < und > auch für die englische Sprache unsinnig.
> Alle stdio-Operationen gemäß Standard arbeiten auf dem Typ `char'> und sind damit unverträglich mit sowohl `signed char' als auch> `unsigned char'.Seufz
Wenn es doch nur so wäre. Die Funktionen, die mit Strings arbeiten,
benutzen [const] char*, aber die, die mit einzelnen Zeichen arbeiten,
verwenden zur zusätzlichen Verwirrung int. Beispiel: int putchar(int c);
Aber es kommt noch besser: Diese Funktionen (nicht nur die
I/O-Funktionen, sondern auch Sachen wie toupper() oder isalpha() - eben
alles, was einzelne Zeichen verarbeitet) erwarten vorzeichenlose Werte,
auch dann, wenn char vorzeichenbehaftet ist. Man muß streng nach Norm
erst nach unsigned char casten! Die korrekte Verwendung von toupper
sieht also so aus:
1
charc='x';
2
c=toupper((unsignedchar)c);
Ohne den Cast ist das Verhalten formal undefiniert.