Hallo Leute.
Habe eine Frage zu folgendem Char Array
char werte[5] = {
0xff,
0x00,
0x7e,
0x00
0x00
};
Ich dachte immer char wäre signed, d.h. man kann werte von -128 bis 127
oder so ähnlich reinschreiben. hier wird aber der wert 255
reingeschrieben. Wie geht das? Müsste man nicht unsigned char nehmen?
Gruß M.H.
wenn ich aber 0 reinschreibe, steht dann in der Speicherstelle 0000
0000
oder eben irgendwie sowas: 1000 0000.
Weil signed ja die null in der mitte hat. woher weiss der compiler,
welche der beiden möglichkeiten er reinschreiben soll, wenn ich
schreibe:
char c;
c = 0;
Gruß M.H.
M. H. schrieb:> Weil signed ja die null in der mitte hat. woher weiss der compiler,> welche der beiden möglichkeiten er reinschreiben soll, wenn ich> schreibe:>>> char c;
0 ist 0 egal ob es signed oder unsigned ist.
char ist weder signed noch unsigned, es entscheidet sich erst wenn man
etwas damit anstellt.
In meinem Tutorial steht:
Bits Min Max
char, signed char: 8 -128 127
Laut dieser Definition dürfte der wert 255/FF nicht zulässig sein. Oder
wird diese "Wertung" erst beim verrechnen vorgenommen?
Gruß M.H.
Edit:
Also wird der Wert 255 beim Verrechnen als 127 gewertet?
M. H. schrieb:> Laut dieser Definition dürfte der wert 255/FF nicht zulässig sein.
nein 255 is das gleich wie -127 - da gibt es keinen binären unterschied.
(unsigned char)255 = 255
(signed char)255 = -127
M. H. schrieb:> In meinem Tutorial steht:>> Bits Min Max> char, signed char: 8 -128 127
Dein Tutorial ist falsch.
Ob char signed oder unsigned ist entscheidet der Compilerbauer. Und oft
genug gibt es einen Compilerschalter mit dem man das überschreiben kann.
Am besten gewöhnst du dir an, dass du 3(!) unterschiedliche Datentypen
hast:
unsigned char
signed char
char
char muss logischerweise eines von den beiden anderen sein. Verwenden
wirst du char aber nur dann, wenn es keine Rolle spielt ob mit oder ohne
Vorzeichen. Also bei reiner Textverarbeitung. Wenn du einen kleinen
Integer brauchst dann ist IMMER entweder signed char oder unsigned char
der Datentyp der Wahl. Du willst es nicht dem Compilerbauer überlassen,
zu entscheiden, wie das sein soll.
Peter II schrieb:> M. H. schrieb:>> Laut dieser Definition dürfte der wert 255/FF nicht zulässig sein.> nein 255 is das gleich wie -127 - da gibt es keinen binären unterschied.
Fast.
-1
(wenn wir 2-er Komplement zugrunde legen, was wohl der Fall sein wird)
Also kann ich ein signed Char von -128 bis 127 benutzen
ein unsigned Char von 0 bis 255
und ein char ist einfach ganz normal so wie in assembler auch von 0-255
oder?
M. H. schrieb:> Laut dieser Definition dürfte der wert 255/FF nicht zulässig sein.
Der Wert an sich ist egal.
Entscheidend ist das Bitmuster, welches im Byte gespeichert ist.
Und erst bei der Verwendung entscheidet sich dann, ob dieses Bitmuster
als mit oder ohne Vorzeichen aufgefasst wird. Denn dem Bitmuster selber
kann man ohne nährere Information (zb der Zusatzinfo, dass die Variable
signed oder unsigned ist) nicht ansehen ob das jetzt eine positive Zahl
(zb 255) oder eine negative Zahl (-1) sein soll. Beide haben dasselbe
Bitmuster.
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Arithmetik8#8-Bit_Arithmetik_ohne_Ber.C3.BCcksichtigung_eines_Vorzeichens
M. H. schrieb:> und ein char ist einfach ganz normal so wie in assembler auch von 0-255
Sagen wir lieber 0x00 bis 0xff oder 0b00000000 bis 0b11111111
Denn char ist i.A signed. Muss es aber nicht, ist Compiler und
Einstellungsabhängig.
M. H. schrieb:> Also kann ich ein signed Char von -128 bis 127 benutzen
Ja
>> ein unsigned Char von 0 bis 255
Ja
>> und ein char ist einfach ganz normal so wie in assembler auch von 0-255
NEIN
Ein char ist weder noch. Lass das char zum rechnen fallen! char benutzt
du in Umgebungen wie
char Text[] = "Hallo World";
Da ist es wurscht, ob char signed oder unsigned ist. char nimmst du,
wenn du es mit Zeichen oder Zeichenketten zu tun hast. Wenn du aber
rechnen willst, dann ist char für dich tabu! Dann gibts nur noch signed
char oder unsigned char. Alles andere ist: ask for trouble.
Das funktioniert aber auch:
signed char c;
c = 255;
Da steht dann aber 1111 1111 drin!
Das ist aber wenn man es als signed auswertet nicht 255!!!
Wie ist das möglich?
M. H. schrieb:> signed char c;>> c = 255;>> Da steht dann aber 1111 1111 drin!
Ja, was soll denn der arme µC dagegen machen?
Da ist ein Byte, das hat 8 Bit und das du c nennst.
Da ist die 255, das sind auch 8 Bit. Alle 1
Und du weist das eine dem anderen zu.
Was soll er denn tun, ausser die eine Bitrepäsentierung in die 8
Speicherbits zu schreiben? Er kann sich doch nicht wehren! Dem µC ist
das doch völlig wurscht was du hinter den Bytes siehst. Wenn du (auf
tiefer loghscher Ebene gesehen) einer Bytespeicherzelle einen Wert
zuweist, dann macht der das auch. Du als Programmierer bist der Boss. Du
musst wissen welche Anweisungen du gibst. Der µC ist nur der Knecht, der
Bits durch die Gegend schaufelt.
> Das ist aber wenn man es als signed auswertet nicht 255!!!
Natürlich nicht. Denn wenn man diese 8 Bit als signed interpretiert,
dann ist das eben -1 (und nicht 255, weil 255 nicht darstellbar ist mit
7 Bit und einem Vorzeichenbit)
woher weiss der Compiler, ob das was auf der rechten seite steht, die
absoluten bitpositionen repräsentiert oder, ob das ganze erst
umgerechnet werden muss:
c = -1 // Führt zu 1111 1111 in der speicherzelle
c = 255 //Führt auch zu 1111 1111 in der Zelle.
Beim ersten wirds nicht direkt übernommen aber beim Zweiten schon. wann
macht der was?
Liebe Leute hier,
bei euch fehlen die basics:
Ein char ist bei diesen Compilern üblicherweise ein Byte.
Ein Byte kann 256 Werte enthalten, von 0x00 ... 0xFF.
Wird es als "unsigned char" ausgewertet, so geht der Zahlenbereich
von 0 (Null) bis 255 (dezimal).
Wird es als "signed" ausgewertet, so geht der Zahlenbereich
von -128 bis +127, wobei sozusagen die "Null" in der Mitte liegt.
0xFF 0xFE ... 0x81 0x80 0x7F 0x7E ... 0x01 0x00 (hex)
------------------------------------------------------------------
-1 -2 -127 -128 +127 +126 +1 Null (dezimal)
"signed"
255 254 129 128 127 127 1 Null (dezimal)
"unsigned"
So ist das jedenfalls lt. ANSI
Ich kenne keine anderen einstellungsabhängigen Varianten.
M. H. schrieb:> woher weiss der Compiler, ob das was auf der rechten seite steht, die> absoluten bitpositionen repräsentiert
Ganz einfach:
Der Compiler führt über die Datentypen Buch.
Auf der rechten Seite er Zuweisung steht ein Ausdruck. Der hat neben
einem Wert auch einen Datentyp (so wie in der Physik alles eine Einheit
hat). Wenn es dann darum geht, den Wert des Ausdrucks zuzuweisen, sieht
sich der Compiler die linke Seite datantyp mässig an, er sieht sich die
rechte Seite datentypmässig an und entscheidet anhand der C-Regeln ob es
etwas zu tun gibt. Wenn nicht dann schnappt er sich das Bitmuster von
der rechten Seite und sorgt dafür, dass genau dieses Bitmuster auf die
links spezifizierte Speicherzelle übrtragen wird.
Und genau darmu ist es auch so wichtig, dass man die C-Regeln kennt,
nach welchen eventuelle Anpassungen oder Umwandlungen vorgenommen
werden.
Im konkreten Fall
unsigned char c = 255;
links ist ein unsigned char, das ist schon mal klar.
rechts steht ein int. Also ein signed int.
Das passt also erst mal nicht.
Also wird aus dem signed int, erst mal ein unsigned int gemacht. Damit
sind links und rechts schon mal signed/unsigned gleich.
links ist ein 8-Bit Datentyp, rechts einer mit 16 Bit. Also werden die
obersten 8 Bit unter den Tisch fallen gelassen und du unteren 8 Bit
zugewiesen.
signed char c = 255;
links steht ein signed char
rechts ein signed int
signed/unsigned mässig passt das schon mal. links sind 8 Bit, rechts 16.
Also werden von der rechten Seite die obersten 8 Bit unter den Tisch
fallen gelassen.
(Immer mit der Annahme: char hat 8 Bit, int hat 16 Bit)
Alle zahlen über 127 entsprechen den negativen, ich kann aber dann
trotzdem positive zahlen wie z.B 255 reinschreiben, die sind dann halt
bei der verarbeitung negativ. Die anderen (positiven) Zahlen haben aber
bei signed und unsigned die selben bitmuster. ich glaug langsam
dämmerts
M. H. schrieb:> Alle zahlen über 127 entsprechen den negativen, ich kann aber dann> trotzdem positive zahlen wie z.B 255 reinschreiben, die sind dann halt> bei der verarbeitung negativ. Die anderen (positiven) Zahlen haben aber> bei signed und unsigned die selben bitmuster.
Genau das ist der springende Punkt:
Es geht nur um das Bitmuster, welches in der Speicherzelle steht.
Nur: bei der Weiterverarbeitung, zb wenn der s/u char wieder auf einen
16 Bit int aufgeblasen werden muss, dann wird im Falle signed eine
Erweiterung auf 16 Bit mit Berücksichtigung eines Vorzeichens gemacht
und im anderen Fall (unsigned) eben nicht. Bei unsigned sind alle Bits
so wie Gott sie schuf, ohne irgendwelche Sonderbehandlungen. Zb. eine
Erweiterung von 8 auf 16 Bit bedeutet bei unsigned einfach nur, dass
links 8 Stück 0 Bits ergänzt werden, weil man ja führende 0-en immer
links anhängen darf soviel man will, ohne dass sich der Zahlenwert
ändert.
Das Bitmuster ist immer gleich. Aber die weitere Behandlung
unterscheidet sich, je nachdem ob du die Variable signed oder unsigned
definiert hast.
malzeit
wem die char int, ... zu kompliziert ist und nicht weis ob das ding nun
8 oder 16 oder doch 32 bit hat, sollte lieber gleich
uint8_t verwenden. damit ist sollte klar sein das es ein Unsigned int
mit 8 bit ist. feierabend.
char definiert ein zeichen, ist platform/Programiermodell abhängig, kann
8 als auch 16 bit sein. Es soll Maschienen geben, die können nicht mit 8
bit rechnen / war damals nicht vorgesehen.
ein int kann je nach Platform/Programmiermodell 16 32 oder 64 bit sein.
Erich schrieb:> Liebe Leute hier,> bei euch fehlen die basics:>> Ein char ist bei diesen Compilern üblicherweise ein Byte.> Ein Byte kann 256 Werte enthalten, von 0x00 ... 0xFF.>> Wird es als "unsigned char" ausgewertet, so geht der Zahlenbereich> von 0 (Null) bis 255 (dezimal).>> Wird es als "signed" ausgewertet, so geht der Zahlenbereich> von -128 bis +127, wobei sozusagen die "Null" in der Mitte liegt.>>> 0xFF 0xFE ... 0x81 0x80 0x7F 0x7E ... 0x01 0x00 (hex)> ------------------------------------------------------------------> -1 -2 -127 -128 +127 +126 +1 Null (dezimal)> "signed">> 255 254 129 128 127 127 1 Null (dezimal)> "unsigned">> So ist das jedenfalls lt. ANSI
Und das alles kannst du auch mit Zitaten aus den entsprechenden ANSI
Dokumenten belegen.
Wo steht denn eigentlich, dass ein char (egal ob signed oder unsigned) 8
Bit haben muss? Wo steht in den ANSI Dokumenten, dass mit 2-er
Komplement gearbeitet werden muss?
(Brauchst nicht nachsehen. Es steht nirgends. Ganz im Gegenteil, das
wird explizit offen gelassen. Bei den Bitbreiten gibt es gewisse
Mindestanforderungen, aber 2-er Komplement wird zb nirgends gefordert.
Alte IBM Maschinen hatten eine ganz andere Repräsentierung des
Vorzeichens)
> Ich kenne keine anderen einstellungsabhängigen Varianten.
Dann kennst du offenbar nicht viele.
Malzeit,
kommt auf das verwendete Programmiermodell an. hat etwas mit der
entwicklung der Maschienen zu tun und der code compatibilität.
Ein pointer wurde früher teilweise als unsigned int definiert. auf 32bit
war der 32bit gross. wird das nun für 64bit übersetzt und bleibt int
32bit dann past der pointer nicht mehr rein und du hast den salat. der
pointer zeigt ggf ganz wo anders hin.
http://www.unix.org/version2/whatsnew/lp64_wp.html
und zum zweier komplement. es ist nun mal die einfachste variante damit
zu rechnen.
- du brauchst kein explizietes subtraktions werk, sondern kanst
subtraktionen mit dem additionswerk durchführen.
- der untere zahlenbeich ist immer gleich egal ob signed oder unsigned.
- es kommen keine doppelten zahlen vor / der ganze zahlenraum wird
ausgeschöpft. (z.B. einerkomplement mit der positiven / negativen 0)
und zu standard. dem ist es föllig egal, da das HW abhängig ist. und die
machen das so wie es ihnen gefällt. wenn IBM meint ne bessere
möglichkeit gefunden zu haben, dann ist dem so. ändert aber nichts an
dem C programm.
gruss
Kern der Sache: Hexadezimalwerte werden in der Informatik fast immer
vorzeichenlos genutzt, um damit Bitfolgen und nicht etwa numerische
Größen abzubilden.
... Hat jemand ein Beispiel für einen Compiler bei dem char tatsächlich
unsigned ist?
... Dass Pointer und Ints immer "type punnable" sind halte ich für einen
gefährlichen Trugschluss, der wohl aus Notwendigkeit entstanden ist weil
man sich nie auf eine Syntax ala Turbo-Pascal "ABSOLUTE" für C geeinigt
hat und sich damit das casten von Ints als Standardlösung durchgesetzt
hat.
Und auch Pointer zu Pointer casten wird nicht immer gut gehen - siehe
far/near in Real Mode x86 (unterschiedliche Größen des Pointers
selbst!), siehe idata/xdata auf dem 8051 (unterschiedliche
Addressräume!), siehe diverse RISC/DSP-Architekturen bei denen wg
Alignment bestimmte Addressen für bestimmte Wortlängen schlichtweg
unzulässig sind...
...Wer mal 16-Bit C-Code bei denen short und int austauschbar genutzt
wurden auf ein 32-Bit System portiert hat ist auch vorsichtig geworden
was Annahmen über Datentypen angeht.
@Martin wie willst Du zB in besagter 16-Bit Umgebung garantieren dass
ein 16-Bit(!) Integer einen gültigen far-Pointer ergibt?
Das mit dem gcc ARM ist in der Tat interessant - warum, ist das nativ so
auf dem ARM?
Andy D. schrieb:> Kern der Sache: Hexadezimalwerte werden in der Informatik fast immer> vorzeichenlos genutzt, um damit Bitfolgen und nicht etwa numerische> Größen abzubilden.
richitg.
Drumm ist der Datentyp für 0xFF zb auch unsigned int. Der für 255 ist
aber signed int.
> Und auch Pointer zu Pointer casten wird nicht immer gut gehen - siehe> far/near
Im C-Standard gibt es kein near und far.
Du schmeisst schon wieder Standard-C und eine spezielle
C-Implementierung durcheinander.
Andy D. schrieb:> @Martin wie willst Du zB in besagter 16-Bit Umgebung garantieren dass> ein 16-Bit(!) Integer einen gültigen far-Pointer ergibt?
Uninteressant, da es in C keinen far Pointer gibt.
Wenn ein Hersteller so etwas einbaut, dann legt er die Regeln fest wie
das geschieht.
Lass uns aber in erster Linie daran halten, was der C-Standard vorgibt.
Wenn ein Neuling diesen Umfang beherrscht UND dann noch weiß wann und
aus welchem Grund sein spezielles System dafür eine Sonderregelung
getroffen hat, dann ist schon etwas erreicht.
XYZ schrieb:> malzeit
Was sollen wir denn malen?
> wem die char int, ... zu kompliziert ist und nicht weis ob das ding nun> 8 oder 16 oder doch 32 bit hat, sollte lieber gleich uint8_t verwenden.> damit ist sollte klar sein das es ein Unsigned int mit 8 bit ist.> feierabend.
Außer bei Lowlevel-I/O braucht man aber eigentlich nie einen Integer,
der zwingend exakt 8 Bit breit sein muß.
> char definiert ein zeichen, ist platform/Programiermodell abhängig, kann> 8 als auch 16 bit sein.
Aber er ist der kleinstmögliche Datentyp. Auf einem Compiler, wo char 16
Bit breit ist, gibt es kein uint8_t. Und gerade dann ist es ein Vorteil,
wenn man nicht überall die Breite unnötigerweise auf exakt 8 Bit
gezwungen hat.
> Es soll Maschienen geben, die können nicht mit 8 bit rechnen / war damals> nicht vorgesehen.ARM kann auch nicht mit 8 Bit rechnen, aber er kann
8-Bit-Speicheroperationen durchführen, und das ist eher wesentlich.
> ein int kann je nach Platform/Programmiermodell 16 32 oder 64 bit sein.
oder alles andere, was nicht kleiner als 16 ist.
XYZ schrieb:> und zum zweier komplement. es ist nun mal die einfachste variante damit> zu rechnen.
Deshalb ist es auch heute das meistverwendete System für
vorzeichenbehaftete Rechnung, aber trotzdem nicht das einzige, das es in
Prozessoren gibt und auch nicht das einzige, das in C erlaubt ist.
> - der untere zahlenbeich ist immer gleich egal ob signed oder unsigned.
Das ist in C auch vorgeschrieben.
> - es kommen keine doppelten zahlen vor / der ganze zahlenraum wird> ausgeschöpft. (z.B. einerkomplement mit der positiven / negativen 0)
Es gibt Berechnungen, die eine Unterscheidung der positiven und
negativen 0 brauchen. Deshalb gibt's die bei Floating Point auch.
Martin schrieb:> Und daß man ints in Pointer auf Daten casten kann, ist sehr wohl> garantiert.
Daß man das kann, ist garantiert, aber nicht garantiert ist die Art der
Zuordnung, also also aus welchem int-Wert welche Adresse wird. Außerdem
ist nicht garantiert, daß man damit jeden möglichen Zeigerwert erzeugen
kann und daß man verlustlos einen Zeiger in einen int und wieder zurück
casten kann. Dafür gibt's intptr_t.
@kbuchegg das glaube ich erst wenn mir jemand die entsprechende Stelle
im Standard nennt. Wie soll das bitte erst auf 64-Bit Systemen
funktionieren, ausser in Implementationen auf denen int gleich long long
ist?
@Martin OK, hab ich mich falsch ausgedrückt. Mit "kann man casten"
meinte ich "mit definiertem und sicherem Effekt", nicht "mit einem cast
kann man ein type punning erzwingen das dazu noch semantisch
fehlschlagen wird".
Andy D. schrieb:> @kbuchegg das glaube ich erst wenn mir jemand die entsprechende Stelle> im Standard nennt. Wie soll das bitte erst auf 64-Bit Systemen> funktionieren, ausser in Implementationen auf denen int gleich long long> ist?
Welche Aussage glaubst du nicht? Ich verlier gerade den Überblick.
Andy D. schrieb:> @kbuchegg das glaube ich erst wenn mir jemand die entsprechende Stelle> im Standard nennt.
Was glaubst du nicht?
Das es in Standard-C keine far Pointer gibt?
Nimm dir den C-Standard, such nach "far" und sieh nach, ob an der
Fundstelle von Pointern die Rede ist.
> Wie soll das bitte erst auf 64-Bit Systemen> funktionieren,
Wie soll was auf 64-Bit Systemen funktionieren?
> Wie soll das bitte erst auf 64-Bit Systemen> funktionieren
Der C-Standard kennt auch keine 64-Bit Systeme.
ALs Grundregel kannst du immer davon ausgehen: Sobald irgendetwas in
irgend einer Weise hardwareabhängig oder sonst irgendwie
systemspezifisch ist, verliert der C-Standard kein Wort darüber. Der
C-Standard kennt keine Monitore oder Tastaturen. Er kennt keine
Diskettem oder CD oder USB-Sticks oder Festplatten. Er kennt keine
definitiven Bitbreiten und auch keine Charater-Codierungen (ASCII). Er
lässt es der Hardware frei, wie sie negative von positiven Zahlen
unterscheidet oder wie sie damit rechnet. Der C-Standard legt fest
welches sichtbare Verhalten C-Konstrukte erzeugen müssen, aber er sagt
nichts darüber aus wie dieses Verhalten konkret zu erzielen ist. Für
Datentypen gibt es diverse Mindestanforderungen, das wars dann aber auch
schon.