Forum: Compiler & IDEs C++ Datentypen (Definitionen) avr-gcc


von Lars Lochmann (Gast)


Lesenswert?

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...

von Klaus F. (kfalser)


Lesenswert?

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.

von Uhu U. (uhu)


Lesenswert?

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.

von Simon K. (simon) Benutzerseite


Lesenswert?

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

von Uhu U. (uhu)


Lesenswert?

> Ich finde das ein wenig inkonsistent.

Sehe ich auch so... Nur ist es doch sicherlich lehrreich, 
herauszufinden, warum der Compiler meckert, oder?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Uhu U. (uhu)


Lesenswert?

Na das hätte doch Lars selber herausfinden sollen... Daß du das weißt, 
ist nichts neues.

von Klaus F. (kfalser)


Lesenswert?

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.

von Uhu U. (uhu)


Lesenswert?

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.

von Lars Lochmann (Gast)


Lesenswert?

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?

von Uhu U. (uhu)


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

> 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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Uhu U. (uhu)


Lesenswert?

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?

von Rolf Magnus (Gast)


Lesenswert?

> 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.

von Lars Lochmann (Gast)


Lesenswert?

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?

von Johannes M. (johnny-m)


Lesenswert?

@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).

von Uhu U. (uhu)


Lesenswert?

@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.

von Uhu U. (uhu)


Lesenswert?

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.

von Johannes M. (johnny-m)


Lesenswert?

@Uhu:
Da haste natürlich recht. Und das ist vermutlich auch genau der Grund 
für die Compiler-Option "[X] char als unsigned behandeln"...

von Uhu U. (uhu)


Lesenswert?

Auf jeden Fall ist es der Grund, warum ich signed char als Standardtyp 
für char nicht mag.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

@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
void func(unsigned char c) {}
2
void func(signed char c)   {}
3
4
int main()
5
{
6
    char c = '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
void func(unsigned int i) {}
2
void func(signed int i)   {}
3
4
int main()
5
{
6
    int i = 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.

von Rolf Magnus (Gast)


Lesenswert?

> 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
char c = 'x';
2
c = toupper((unsigned char)c);

Ohne den Cast ist das Verhalten formal undefiniert.

von Uhu U. (uhu)


Lesenswert?

@Jörg Wunsch
> Unser Waldvogel schrieb:

Der Uhu ein Waldvogel? Na du hast komische Vorstellungen...
http://de.wikipedia.org/wiki/Uhu_%28Vogel%29#Lebensraum

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.