C - casten
Bio
- programmiertechnik
0 Christian Seiler0 Bio
0 Der Martin
0 Der Martin
0 Bio
0 Bio
0 Christoph Gärtner
Sup!
So wie's aussieht (so wie ich den C-Standard verstehe), kann man relativ ungefährdet signed integer auf unsigned integer casten - auch wenn der (Rang des) unsigned integer gleich groß oder kleiner als der (Rang des) signed integer ist und darum der signed-Wert eigentlich nicht in den unsigned umgeformt werden kann, ohne das Vorzeichen oder mehr zu verpfuschen.
sint32 a = 0xffffffff;
uint32 b = (uint32)a; a = 0xffffffff
Wenn man jedoch in die andere Richtung casten will, ist das Ergebnis angeblich "implementation-specific".
uint32 a = 0xffffffff;
sint32 b = (sint32) a; b = ???
Es scheint allerdings keine Implementierung zu geben, wo nicht einfach die Bit-Repräsentation als signed integer interpretiert wird.
Oder hat jemand schonmal irgendein Problem beim casten von unsigned int auf (vom Rang gleich grosses oder kleineres) signed int erlebt?
Gruesse,
Bio
Hallo Bio,
Warum willst Du das überhaupt machen? Vielleicht lässt sich ja dieses "implementation specific" (was ja nix anderes heißt wie: wenn's knallt, bist Du selbst Schuld) komplett umgehen über einen anderen Weg?
Viele Grüße,
Christian
Sup!
Tja also... aus einem Byte-Strom müssen mitten in diesem Strom verstreute Bits zu beliebigen signed/unsigned-Datentypen zusammengebastelt werden (Frag' nicht warum das sinnvoll ist, das ist einfach eine Kundenanforderung).
Es können also z.B. 19 Bits ("O")folgendermaßen über 3 Byte verstreut ankommen:
xxxOOOOO
OOOOOOOO
OOOOOOxx
Damit der Algorithmus alle Arten von Typen zusammensetzen kann, werden die Bits erstmal in einen uint32 "eingelesen" (mit viel shiften und and-en) und dann zum gewünschten Datentypen gecastet.
Es wäre aber anscheinend "besser", wenn man in einen sint32 einlesen würde, weil casten von sint32 nach uint32 im C-Standard allem Anschein nach besser spezifiziert ist als casten von uint32 nach sint32. (In meinem Kernighan-Richie ist es in Anhang A.6.2, "Integer-Umwandlung", beschrieben).
Man könnte natürlich den Algorithmus auch sechsmal schreiben, um ohne casten die Datentypen u/s-int8/16/32 einlesen zu können; das wäre aber nicht wirklich effizient, denn der Kram läuft auf einem embedded system, und Codesize ist relevant.
Ähnlich diesem Problem gibt es auch noch eine Funktion, die auf alle diese Datentypen Vergleichsoperationen anwenden soll. Die will ich auch nicht unbedingt sechsmal schreiben.
Und um eine generische Funktion schreiben zu könne, müsste ich wieder etwas casten - ggf. auch uint32 nach sint32.
Gruesse,
Bio
Hallo,
So wie's aussieht (so wie ich den C-Standard verstehe), kann man relativ ungefährdet signed integer auf unsigned integer casten
natürlich, die *Interpretation* eines Haufens von 16/32bits Information ist schließlich immer Sache des verarbeitenden Programmteils.
auch wenn der (Rang des) unsigned integer gleich groß oder kleiner als der (Rang des) signed integer ist und darum der signed-Wert eigentlich nicht in den unsigned umgeformt werden kann, ohne das Vorzeichen oder mehr zu verpfuschen.
Hä? Was meinst du mit "Rang"? Wenn Typ U und Typ V beide auf dem Grundtyp Integer beruhen (also egal ob mit oder ohne Vorzeichen, egal ob 8,16,32bit, egal ob int, char, enum), dann bedeutet ein Typecast von U auf V: Nimm die binäre Repräsentation von U und interpretiere sie nach dem Muster von V.
sint32 a = 0xffffffff;
uint32 b = (uint32)a; a = 0xffffffff
Genau. a war vorher -1, b ist jetzt 4294967295 (=2^32-1).
Wenn man jedoch in die andere Richtung casten will, ist das Ergebnis angeblich "implementation-specific".
Ach?
uint32 a = 0xffffffff;
sint32 b = (sint32) a; b = ???
Das Beispiel ist noch deutlicher: a war 4294967295, b ist jetzt -1.
Es scheint allerdings keine Implementierung zu geben, wo nicht einfach die Bit-Repräsentation als signed integer interpretiert wird.
Eben, sag ich doch.
Oder hat jemand schonmal irgendein Problem beim casten von unsigned int auf (vom Rang gleich grosses oder kleineres) signed int erlebt?
Nein - abgesehen davon, dass ich nicht hundertprozentig sicher bin, was du mit dem ominösen Begriff "Rang" meinst, war das in jedem Fall von 32bit-Windows bis runter zu 8bit-Mikrocontrollern problemlos.
So long,
Martin
Moin,
Wenn man jedoch in die andere Richtung casten will, ist das Ergebnis angeblich "implementation-specific".
das einzige, was AFAIK "implementation-specific" ist und sein darf, ist das Verhalten beim Casten eines vorzeichenbehafteten Werts auf einen Typ mit größerer Wortbreite. Dabei ist nicht genau festgelegt, ob das Vorzeichenbit erweitert oder die zusätzlichen Bits einfach mit Nullen aufgefüllt werden.
Bei Typen mit gleicher Wortbreite wird aber einfach die binäre Darstellung unverändert übernommen und nur dem neuen Zieltyp entsprechend interpretiert.
Ciao,
Martin
Sup!
Genau das ist anscheinend genau spezifiziert, schon im Kernighan Richie. Bei negativen Werten wird links mit 1en aufgefüllt, bei positiven mit 0en.
Gruesse,
Bio
Hi,
Genau das ist anscheinend genau spezifiziert, schon im Kernighan Richie. Bei negativen Werten wird links mit 1en aufgefüllt, bei positiven mit 0en.
dann widerspricht genau da die Praxis der Theorie. Ich habe nämlich bei etlichen Compilern, vor allem im µC-Bereich, schon erlebt, dass ungeachtet des tatsächlichen Vorzeichens mit Nullen aufgefüllt wurde.
Ciao,
Martin
Sup!
natürlich, die *Interpretation* eines Haufens von 16/32bits Information ist schließlich immer Sache des verarbeitenden Programmteils.
Naja... oder des Compilers oder des Prozessors; das Programm weiss eigentlich nichts darüber, ob die Maschine im Binär-System arbeitet; sie könnte auch mit BCD oder im 7er-System arbeiten (theoretisch).
Hä? Was meinst du mit "Rang"? Wenn Typ U und Typ V beide auf dem Grundtyp Integer beruhen (also egal ob mit oder ohne Vorzeichen, egal ob 8,16,32bit, egal ob int, char, enum), dann bedeutet ein Typecast von U auf V: Nimm die binäre Repräsentation von U und interpretiere sie nach dem Muster von V.
Als Rang bezeichnet man die Bitbreite. Also haben ein uint8 und ein signed char mit 8 Bit Breite den gleichen Rang (also quasi den gleichen Informationsgehalt).
Mein Problem ist einfach, dass im C-Standard nirgends steht, dass es funktionieren muss, wenn ich ein unsigned integer auf einen signed integer gleichen Ranges caste.
Gruesse,
Bio
Morgen.
Wenn man jedoch in die andere Richtung casten will, ist das Ergebnis angeblich "implementation-specific".
uint32 a = 0xffffffff;
sint32 b = (sint32) a; b = ???
»»
Es scheint allerdings keine Implementierung zu geben, wo nicht einfach die Bit-Repräsentation als signed integer interpretiert wird.
Mal ganz praktisch Gedacht: Auf welchen obskuren Plattform-Compiler-Kombinationen soll das Ding denn mal laufen? Laut ISO/IEC 9899:TC2, Punkt 6.3.1.3 ist das Verhalten 'implementation-defined', aber wenn du die Ziel-Implementierung kennst...
Ansonsten kannst du auch etwas mit Zeigern spielen, so dass sich am Wert der Variablen nichts ändert, nur an deren Interpretation; bei meinem GNU liefern beide Versionen das Ergebnis -1:
uint32_t a = 0xffffffff;
int32_t b = (int32_t)a; // b = -1
int32_t c = *(int32_t*)(&a); // c = -1
Gruß
Christoph
Nachtrag (erst denken, dann posten ;))
Oder etwas eleganter:
union {
uint32_t asUnsigned;
int32_t asSigned;
} a;
a.asUnsigned = 0xffffffff;
printf("%d",a.asSigned); // -1
Gruß
Christoph
Sup!
Leider soll das ganze auf mehreren Plattformen laufen, und leider sind unions auch auf der Liste der Dinge, die relativ verboten sind.
Es soll nämlich alles Misra-C entsprechen; wird aber wohl nicht ganz hinhauen.
Gruesse,
Bio
Abend.
Ich bin da vielleicht nicht der Kompetenteste, was von x86 abweichende Systeme betrifft (außerdem habe ich keine Ahnung, was unter MISRA-C alles erlaubt ist), aber *(int32_t*)(&a) sollte eigentlich auch dort funktionieren, wo der einfache Cast versagt:
Einfaches Casten heißt ja: nimm den Wert und stelle ihn _wertgleich_ in dem neuen Typ dar - was passiert, wenn das nicht möglich ist, ist dann halt Compiler-Sache
Wenn ich hingegen die Zeiger caste, wird einfach die Speicherstelle anders interpretiert, was das gewünschte Ergebnis liefert, sofern 'Quell'- und 'Ziel'-Plattform (du hast nicht erwähnt, woher die Daten kommen) die gleiche Integer-Kodierung verwenden - um die 'endianness' muss man sich natürlich selbst kümmern, und falls andere Vorzeichenkonventionen verwendet werden (k.A., ob in der Praxis was anderes als two's-complement überhaupt auftritt) führt ja wohl kein Weg daran vorbei, sich die Zahlen über arithmetische Operationen neu zu 'bauen'.
Gruß
Christoph