Forum: Compiler & IDEs [Arduino ESP32] Serial.print() mit negativem char


von Niklas (niklas_djkfghs)


Lesenswert?

Hallo,
ich möchte einen negativen char mit Serial.println() ausgeben, bekomme 
jedoch nicht den richtigen Wert.
1
char cTest = -16;
2
int8_t iTest = -16;
3
4
void setup() {
5
  Serial.begin(115200);
6
}
7
8
void loop() {
9
  Serial.println(cTest, DEC);       //gibt 240
10
  cTest += 20;
11
  Serial.println(cTest, DEC);       //gibt 4
12
  
13
  Serial.println(iTest, DEC);       //gibt -16
14
  iTest += 20;
15
  Serial.println(iTest, DEC);       //gibt 4
16
17
  delay(10000);
18
}

Windows 10
Arduino 1.8.13
ESP32 1.0.5
esptool.py v3.0-dev
Chip is ESP32-D0WD (revision 1)

Was mache ich falsch?
Grüße Niklas

von MKr (Gast)


Lesenswert?

Niklas M. schrieb:
> bekomme jedoch nicht den richtigen Wert.

Was ist denn der "richtige" Wert? Was erwartest du als Ausgabe?

char ist ist vorzeichenloser Wert.
-16 enspricht binär 1111 0000. Als vorzeichenlose Zahl ist das 240. 
Siehe Zeile 9. Addierst du 20, rechnet er 240+15=255, läuft mit +1 über 
zu 0 und es bleiben noch 4 übrig. Siehe Zeile 11.

Mit int passiert das ganze im Prinzip genauso, nur wird das MSB als 
Vorzeichen gedeutet.
Es passiert alles genauso, wie es C soll.

Deswegen:
> Was mache ich falsch?
Du versuchst etwas, dass so keinen Sinn ergibt. Was soll ein negativer 
char sein? Was versuchst du zu erreichen?

von chris_ (Gast)


Lesenswert?

MKr:
>Was ist denn der "richtige" Wert? Was erwartest du als Ausgabe?
>char ist ist vorzeichenloser Wert.

Nicht zwingend:
https://de.wikipedia.org/wiki/Datentypen_in_C#Character

"Ein Character repräsentiert die kleinste adressierbare Einheit in C, in 
der Regel acht Bit. Deshalb wird die Größe von Objekten und Typen oft 
als ganzzahliges Vielfaches eines Characters angegeben. Je nach Compiler 
kann char entweder gleichbedeutend sein mit signed char (meistens -128 
bis 127) oder mit unsigned char (meistens 0 bis 255)."

von MKr (Gast)


Lesenswert?

OK, das mag sein, wenn ich persönlich auch den Sinn des ganzen nicht 
sehe. Will ich eine Ganzzahl darstellen? -> int in irgendeiner 
Ausprägung.
Ein Zeichen? -> Char. Dann folgt es der ASCII-Tabelle und die geht nur 
für 7 Bit, das 8. ist "ü"
Geht es mir, z.B. für die Übertragung, nur um die reine Bitfolge und 
soll das ganze für die verschiedensten Datentypen kompatibel sein, kann 
man das mit char machen. Wobei alle dann nur die reine Bitfolge 
interessiert und deren Deutung an der Stelle irrelevant ist. Damit auch 
Vorzeichen oder eben nicht.
Aber das ist meine persönliche Meinung, die nicht zur Diskussion steht 
und soll nicht Thema dieses Threads sein und hilft dem OT nicht.
Ergo:

MKr schrieb:
> Was versuchst du zu erreichen?

von 900ss (900ss)


Lesenswert?

Er wusste nur nicht, dass char von Compiler mit oder ohne Vorzeichen 
behandelt werden. Das hängt von dem default des Compiler ab oder von der 
Option, die dem Compiler sagt, wie er char behandeln soll.

Ich denke der Test war nur zum Verständnis und ist dafür gut geeignet.

von Niklas (niklas_djkfghs)


Lesenswert?

MKr schrieb:
> Was erwartest du als Ausgabe?
Ich hatte -16 erwartet.
In Konsolenanwendungen mit printf hat das bis jetzt auch immer 
funktioniert. Also z.B. so:
1
char cTest = -16;
2
printf("%d", cTest);

MKr schrieb:
> char ist ist vorzeichenloser Wert.

Ich denke hier liegt mein Fehler. Ich dachte char sei das Gegenstück zu 
unsigned char mit einem Wertebereich von –128 bis +127.


MKr schrieb:
> Will ich eine Ganzzahl darstellen? -> int in irgendeiner
> Ausprägung.
> Ein Zeichen? -> Char.

Danke, das wäre meine nächste Frage gewesen. Ich habe bis jetzt char 
immer für beides benutzt und das Formatierungszeichen im printf 
entsprechend angepasst.

MKr schrieb:
> Was versuchst du zu erreichen?

Ich habe keine Aufgabe oder so, versuche Programmieren zu lernen.

von MKr (Gast)


Lesenswert?

Niklas M. schrieb:
> Ich habe keine Aufgabe oder so, versuche Programmieren zu lernen.
Sehr löblich und viel Erfolg dabei!

Niklas M. schrieb:
> Ich dachte char sei das Gegenstück zu
> unsigned char mit einem Wertebereich von –128 bis +127.
Mir ist bis jetzt noch kein Fall untergekommen, in dem signed char 
verwendet wurde oder sinnvoll gewesen wäre. Zugegeben, muss das nichts 
heißen. Mir fällt aber auch kein Fall ein. Es muss welche geben, sonst 
gäbe es das Konstrukt nicht.

Niklas M. schrieb:
> printf("%d", cTest);
An dieser Stelle hast du dem Compiler gesagt, dass er deine Bitfolge als 
int interpretieren soll und damit wurde es eine vorzeichenbehaftete 
Ganzzahl. Es war kein char mehr. Das erklärt deine Verwirrung

Niklas M. schrieb:
> Ich habe bis jetzt char
> immer für beides benutzt und das Formatierungszeichen im printf
> entsprechend angepasst.
Gewöhne dir an, für deine Daten den passenden Datentyp zu verwenden. Das 
macht zum einen deine Programme verständlicher und übersichtlicher und 
zum anderen kann dir der Compiler dann helfen, Fehler zu vermeiden, wenn 
du Typecasts machst. Zumindestens kann er dir dann Hinweise geben. 
Übersichtlicher z.B. wegen:
1
char a='a';
2
char b='b';
3
char c=a+b;
Der Compiler macht das ohne Probleme. Aber welchen Sinn hat es (an 
dieser Stelle), Buchstaben zu addieren?
1
uint8_t a=97;
2
uint8_t b=98;
3
uint8_t c=a+b;
ergibt da schon mehr Sinn

von Manfred (Gast)


Lesenswert?

MKr schrieb:
> Gewöhne dir an, für deine Daten den passenden Datentyp zu verwenden.

Genau dafür hasse ich C++, diese scheiß Deklaration der Datentypen hat 
mich schon Stunden beschäftigt.

von Andreas B. (bitverdreher)


Lesenswert?

MKr schrieb:
> Gewöhne dir an, für deine Daten den passenden Datentyp zu verwenden.
+1
Und lerne die verschiedenen Datentpen und deren Anwendung zuerst!

Manfred schrieb:
> Genau dafür hasse ich C++, diese scheiß Deklaration der Datentypen hat
> mich schon Stunden beschäftigt.

Darin unterscheiden sich eben diejenigen, die programmieren können. Ohne 
Kenntnis der Datentypen wirst Du mit keiner Programmiersprache glücklich 
werden.

von Rolf M. (rmagnus)


Lesenswert?

Niklas M. schrieb:
> MKr schrieb:
>> char ist ist vorzeichenloser Wert.
>
> Ich denke hier liegt mein Fehler. Ich dachte char sei das Gegenstück zu
> unsigned char mit einem Wertebereich von –128 bis +127.

Das wäre signed char. Prinzipiell ist char einfach nur der kleinste 
Integer-Typ. Aber es gibt zu den anderen, also short int, int, long int 
und long long int einen Unterschied: Während bei diesen ohne einen 
signed/unsigned-Präfix immer ein signed impliziert wird (also signed 
short int und short int sind der selbe Typ),  ist das bei char nicht so. 
Da sind char, signed char und unsigned char drei verschiedene Typen. 
char kann dabei je nach Compiler die selbe Darstellung wie signed oder 
wie unsigned haben. Der Typ ist eigentlich auch nicht zum Rechnen 
gedacht, sondern nur dazu, ein Zeichen zu speichern. Dahrer gilt: Für 
Text immer char, wenn man rechnen will dagegen explizit signed oder 
unsigned char.

MKr schrieb:
> Mir ist bis jetzt noch kein Fall untergekommen, in dem signed char
> verwendet wurde oder sinnvoll gewesen wäre. Zugegeben, muss das nichts
> heißen. Mir fällt aber auch kein Fall ein. Es muss welche geben, sonst
> gäbe es das Konstrukt nicht.

Das kommt vermutlich in der Praxis eher selten vor, vor allem auf 
nicht-8-Bit-Plattformen, aber prinzipiell ist es nix anderes als bei den 
andern Integer-Typen auch.

> Niklas M. schrieb:
>> printf("%d", cTest);
> An dieser Stelle hast du dem Compiler gesagt, dass er deine Bitfolge als
> int interpretieren soll und damit wurde es eine vorzeichenbehaftete
> Ganzzahl. Es war kein char mehr. Das erklärt deine Verwirrung

Das ist aber hier nicht das Problem, denn int ist in der Regel größer 
als char und damit auch in der Lage, den ganzen Wertebereich davon 
abzudecken, egal ob das nun ein Vorzeichen hat oder nicht. Das Problem 
ist eher, dass auf einer der getesteten Plattformen char ein Vorzeichen 
hat, auf der anderen aber nicht. Genau deshalb soll man char ohne 
(un)signed-Präfix nie zum Rechnen verwenden.

Manfred schrieb:
> MKr schrieb:
>> Gewöhne dir an, für deine Daten den passenden Datentyp zu verwenden.
>
> Genau dafür hasse ich C++, diese scheiß Deklaration der Datentypen hat
> mich schon Stunden beschäftigt.

Statt die Emotionen ausufern zu lassen, wäre es ggf. sinnvoller, 
einmalig etwas Zeit in das Verständnis der Datentypen zu investieren. 
Das ist nun auch keine Raketenwissenschaft. Muss man übrigens bei jeder 
Sprache.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

MKr schrieb:
> Übersichtlicher z.B. wegen:char a='a';
> char b='b';
> char c=a+b;
> Der Compiler macht das ohne Probleme. Aber welchen Sinn hat es (an
> dieser Stelle), Buchstaben zu addieren?uint8_t a=97;
> uint8_t b=98;
> uint8_t c=a+b;
> ergibt da schon mehr Sinn

char nimmt man, wenn man Zeichen bearbeiten möchte.
ein 'a'+'b' mag wenig sinnvoll erscheinen, ein 'a' + 1 erscheint 
sinnvoller und ein 'z'-'a' ist sehr sinnvoll.

Magic Numbers sollte man auch nicht benutzen.

uint8_t ist nur ein typedef für signed char

Das Serial.println erkennt wohl am Typ vom ersten Parameter was es 
machen soll und erkennt bei einem int8_t auch nur ein char.

von Jonas B. (jibi)


Lesenswert?

>uint8_t ist nur ein typedef für signed char

Das "u" vor dem "int" besagt das es unsigned ist.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Jonas B. schrieb:
>>uint8_t ist nur ein typedef für signed char
>
> Das "u" vor dem "int" besagt das es unsigned ist.

Danke.
🙄 warum kann man sich nicht an unwichtigeren Stellen verschreiben 🤷‍♂️

von Niklas (niklas_djkfghs)


Lesenswert?

Guten Morgen,
nachdem mir klar wurde um was es geht, habe ich in meinem Lehrbuch das 
Kapitel zu Datentypen noch einmal nachgeschlagen und muss gestehen, dass 
dort eigentlich die ganze Thematik erklärt wurde.

Asche über mein Haupt, das habe ich damals wohl schlicht überlesen/nicht 
verstanden.

Vielen Dank an alle, die sich die Mühe gemacht haben mir weiterzuhelfen 
:)
Grüße Niklas

von MKr (Gast)


Lesenswert?

Rolf M. schrieb:
> wenn man rechnen will dagegen explizit signed oder
> unsigned char.
Das funktioniert, verschleiert aber an der Stelle den Sinn des Codes. 
Deswegen würde ich es nicht tun.

Dirk B. schrieb:
> uint8_t ist nur ein typedef für signed char
Auch hier würde ich IMMER uint8_t verwenden, weil dadurch impliziert 
wird, dass mit Zahlen und nicht mit Zeichen gerechnet wird. Auch, wenn 
es im Maschinencode das selbe Ergebnis hat.

Dirk B. schrieb:
> char nimmt man, wenn man Zeichen bearbeiten möchte.
> ein 'a'+'b' mag wenig sinnvoll erscheinen, ein 'a' + 1 erscheint
> sinnvoller und ein 'z'-'a' ist sehr sinnvoll.
Völlig korrekt, es gibt Anwendungen, bei denen mit Zeichen gerechnet 
wird. Das wollte ich auch nicht ausschließen. Der OT hat aber keine 
Zeichenmanipulation betrieben, sondern wirklich mit Zahlen gerechnet. 
Deswegen mein "sinnloses" Bespiel und kein sinnvolles

von Rolf M. (rmagnus)


Lesenswert?

MKr schrieb:
> Rolf M. schrieb:
>> wenn man rechnen will dagegen explizit signed oder
>> unsigned char.
> Das funktioniert, verschleiert aber an der Stelle den Sinn des Codes.

Ich sehe nicht, wie da was verschleiert werden sollte. Wenn da explizit 
signed char oder unsigned char steht, heißt das für mich ganz klar, dass 
es nicht um Text geht.

> Dirk B. schrieb:
>> uint8_t ist nur ein typedef für signed char
> Auch hier würde ich IMMER uint8_t verwenden, weil dadurch impliziert
> wird, dass mit Zahlen und nicht mit Zeichen gerechnet wird.

Damit wird auch impliziert, dass es zwingend exakt 8 Bit sein müssen. 
Wenn mir das an der Stelle egal ist und der Typ einfach nur der 
kleinstmögliche sein soll, tut's ein unsigned char auch.

> Dirk B. schrieb:
>> char nimmt man, wenn man Zeichen bearbeiten möchte.
>> ein 'a'+'b' mag wenig sinnvoll erscheinen, ein 'a' + 1 erscheint
>> sinnvoller und ein 'z'-'a' ist sehr sinnvoll.
> Völlig korrekt, es gibt Anwendungen, bei denen mit Zeichen gerechnet
> wird.

Ich würde soweit gehen, zu sagen, dass das nicht stimmt. Ein 'a' ist 
keine Zahl, mit der man rechnen könnte. Natürlich hat es einen 
Zeichencode, mit dem man durchaus rechnen könnte, aber das sind für mich 
zwei verschiedene Dinge. In C und C++ ist diese Unterscheidung leider 
nicht konsequent umgesetzt, aber in anderen Sprachen kann man sie sehr 
deutlich erkennen. In Pascal z.B. gibt es auch einen Typ char, aber mit 
dem kann man nicht rechnen. Ein 'a' + 5 wird mit Fehler quittiert. Man 
kann einen char auch nicht mit einem Zahlenwert initialisieren. Wenn man 
damit rechnen will, muss man das Zeichen erst in einen Integer-Wert 
konvertieren, dann damit rechnen, und wenn es am Ende wieder ein Zeichen 
sein soll, muss man eben wieder nach char zurückwandeln. Ich schlage nun 
vor, in C und C++ das selbe zu machen. Der Unterschied ist hier nur, 
dass diese Sprachen theoretisch auch ein direktes Rechnen mit dem char 
erlauben, aber man sollte dies trotzdem nicht tun.

von Dirk B. (dirkb2)


Lesenswert?

MKr schrieb:
> Auch hier würde ich IMMER uint8_t verwenden, weil dadurch impliziert
> wird, dass mit Zahlen und nicht mit Zeichen gerechnet wird.

Dann sollte man aber uint_least8_t nehmen. Denn das funktioniert auch, 
wenn char keine 8 Bit hat.

von foobar (Gast)


Lesenswert?

> Mir ist bis jetzt noch kein Fall untergekommen, in dem signed char
> verwendet wurde

Die meisten Systeme, insb x86 Linux und Windows, haben signed char als 
default.  Bei PowerPC, ARM und 8-Bittern findet man eher unsigned char.

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.