Welche Plattformen haben etwas anderes als 8-Bit-Char?

Von Zeit zu Zeit weist jemand auf SO darauf hin, dass char (alias ‘byte’) nicht unbedingt 8 Bits lang ist .

Es scheint, dass 8-Bit- char fast universell ist. Ich hätte gedacht, dass es für Mainstream-Plattformen notwendig ist, einen 8-Bit- char zu haben, um seine Lebensfähigkeit auf dem Markt sicherzustellen.

Welche Plattformen verwenden jetzt und historisch ein char , das nicht 8 Bits ist, und warum unterscheiden sie sich von den “normalen” 8 Bits?

Wenn Sie Code schreiben und über plattformübergreifende Unterstützung nachdenken (z. B. für allgemein nutzbare Bibliotheken), welche Art von Überlegung ist es wert, Plattformen mit Nicht-8-Bit- char ?

In der Vergangenheit bin ich auf einige DSPs von Analog Devices gestoßen, für die char 16 Bit ist. DSPs sind ein bisschen wie eine Nischenarchitektur, nehme ich an. (Dann wieder, zu der Zeit Hand-codierten Assembler leicht zu schlagen, was die verfügbaren C-Compiler tun könnten, so habe ich nicht wirklich viel Erfahrung mit C auf dieser Plattform.)

char ist auch 16 Bit auf den Texas Instruments C54x DSPs, die zum Beispiel in OMAP2 aufgetaucht sind. Es gibt andere DSPs mit 16 und 32 bit char . Ich glaube, ich habe sogar von einer 24-Bit-DSP gehört, aber ich kann mich nicht erinnern, was ich getan habe, also habe ich es mir vielleicht vorgestellt.

Eine weitere Überlegung ist, dass POSIX CHAR_BIT == 8 . Wenn Sie also POSIX verwenden, können Sie davon ausgehen. Wenn jemand später Ihren Code in eine POSIX-ähnliche Implementierung portieren muss, die zufällig die functionen verwendet, die Sie verwenden, aber eine andere Größe haben, ist das ihr Pech.

Im Allgemeinen denke ich, dass es fast immer einfacher ist, das Problem zu umgehen, als darüber nachzudenken. CHAR_BIT einfach CHAR_BIT . Wenn Sie einen exakten 8-Bit-Typ int8_t , verwenden Sie int8_t . Ihr Code wird auf Implementierungen, die keine bieten, nicht kompilieren, statt stillschweigend eine Größe zu verwenden, die Sie nicht erwartet haben. Zumindest, wenn ich einen Fall treffen würde, wo ich einen guten Grund hatte, es anzunehmen, dann würde ich es behaupten.

Wenn Sie Code schreiben und über plattformübergreifende Unterstützung nachdenken (z. B. für allgemein nutzbare Bibliotheken), welche Art von Überlegung ist es wert, Plattformen mit Nicht-8-Bit-Zeichen zu geben?

Es ist nicht so sehr, dass es “wert ist, etwas zu berücksichtigen”, da es nach den Regeln spielt. In C ++ zum Beispiel besagt der Standard, dass alle Bytes “mindestens” 8 Bits haben werden. Wenn Ihr Code davon ausgeht, dass Bytes genau 8 Bits haben, verletzen Sie den Standard.

Das mag jetzt albern erscheinen – ” natürlich haben alle Bytes 8 Bits!”, Höre ich Sie sagen. Aber viele sehr kluge Leute haben sich auf Annahmen gestützt, die keine Garantien waren, und dann ist alles kaputt gegangen. Die Geschichte ist voll von solchen Beispielen.

Zum Beispiel nahmen die meisten Entwickler der frühen 90er Jahre an, dass eine bestimmte No-Op-CPU-Timing-Verzögerung, die eine feste Anzahl von Zyklen benötigt, eine feste Taktzeit beanspruchen würde, da die meisten Verbraucher-CPUs ungefähr die gleiche performance hatten. Leider wurden Computer sehr schnell schneller. Dies führte zur Entstehung von Boxen mit “Turbo” -Knöpfen – deren Zweck ironischerweise war, den Computer zu verlangsamen, damit Spiele, die die Zeitverzögerungstechnik verwendeten, mit einer angemessenen Geschwindigkeit gespielt werden konnten.


Ein Kommentator fragte, wo in der Norm steht, dass Char mindestens 8 Bit haben muss. Es ist in Abschnitt 5.2.4.2.1 . Dieser Abschnitt definiert CHAR_BIT , die Anzahl der Bits in der kleinsten adressierbaren Entität, und hat einen Standardwert von 8. Er sagt auch:

Ihre implementationsdefinierten Werte müssen in der Größenordnung (Absolutwert) zu den gezeigten mit dem gleichen Vorzeichen gleich oder größer sein.

Daher ist jede Zahl gleich 8 oder höher für eine Substitution durch eine Implementierung in CHAR_BIT .

Maschinen mit 36-Bit-Architekturen haben 9-Bit-Bytes. Laut Wikipedia enthalten Maschinen mit 36-Bit-Architekturen :

  • Digital Equipment Corporation PDP-6/10
  • IBM 701/704/709/7090/7094
  • UNIVAC 1103 / 1103A / 1105/1100/2200,

Ein paar von denen ich weiß:

  • DEC PDP-10: Variable, aber am häufigsten 7-Bit-Zeichen gepackt 5 pro 36-Bit-Wort oder 9-Bit-Zeichen, 4 pro Wort
  • Steuerdaten-Mainframes (CDC-6400, 6500, 6600, 7600, Cyber ​​170, Cyber ​​176 usw.) 6-Bit-Zeichen, gepackt 10 pro 60-Bit-Wort.
  • Unisys Mainframes: 9 Bits / Byte
  • Windows CE: unterstützt den “char” -Typ überhaupt nicht – erfordert stattdessen 16-bit wchar_t

Es gibt keinen vollständig tragbaren Code. 🙂

Ja, möglicherweise gibt es verschiedene Byte- / Zeichengrößen. Ja, es gibt C / C ++ – Implementierungen für Plattformen mit höchst ungewöhnlichen Werten von CHAR_BIT und UCHAR_MAX . Ja, manchmal ist es möglich, Code zu schreiben, der nicht von der Zeichengröße abhängt.

Fast jeder echte Code ist jedoch nicht eigenständig. ZB schreiben Sie möglicherweise einen Code, der binäre Nachrichten an das Netzwerk sendet (Protokoll ist nicht wichtig). Sie können Strukturen definieren, die notwendige Felder enthalten. Dann musst du es serialisieren. Einfaches binäres Kopieren einer Struktur in einen Ausgabepuffer ist nicht portierbar: Im Allgemeinen kennen Sie weder die Byte-Reihenfolge für die Plattform noch die Struktur-Alignment-Struktur, also enthält die Struktur nur die Daten, aber nicht die Art, wie die Daten serialisiert werden sollen .

OK. Sie können Byte-Reihenfolge-Transformationen durchführen und die Strukturelemente (z. B. uint32_t oder ähnlich) mit memcpy in den Puffer verschieben. Warum memcpy ? Da es viele Plattformen gibt, auf denen es nicht möglich ist, 32-Bit (16-Bit, 64-Bit – kein Unterschied) zu schreiben, wenn die Zieladresse nicht richtig ausgerichtet ist.

Sie haben also schon viel getan, um Portabilität zu erreichen.

Und jetzt die letzte Frage. Wir haben einen Puffer. Die Daten werden an das TCP / IP-Netzwerk gesendet. Ein solches Netzwerk nimmt 8-Bit-Bytes an. Die Frage ist: Von welchem ​​Typ sollte der Puffer sein? Wenn deine Zeichen 9-Bit sind? Wenn sie 16-Bit sind? 24? Vielleicht entspricht jedes Zeichen einem 8-Bit-Byte, das an das Netzwerk gesendet wird, und nur 8 Bits werden verwendet? Oder sind mehrere Netzwerkbytes in 24/16/9-Bit-Zeichen gepackt? Das ist eine Frage, und es ist schwer zu glauben, dass es eine einzige Antwort gibt, die zu allen Fällen passt. Viele Dinge hängen von der Socket-Implementierung für die Zielplattform ab.

Also, worüber ich spreche. Normalerweise kann Code relativ leicht in gewissem Maße portabel gemacht werden . Dies ist sehr wichtig, wenn Sie den Code auf verschiedenen Plattformen verwenden möchten. Die Verbesserung der Portabilität über dieses Maß hinaus ist eine Sache, die viel Aufwand erfordert und oft wenig bringt , da der echte Code fast immer von anderem Code abhängt (Socket-Implementierung im obigen Beispiel). Ich bin sicher, dass für etwa 90% der Code-Fähigkeit, auf Plattformen mit anderen Bytes als 8-Bit zu arbeiten, fast nutzlos ist, denn es verwendet Umgebung, die an 8-Bit gebunden ist. Überprüfen Sie nur die Byte-Größe und führen Sie die Assertion der Kompilierungszeit aus. Sie werden fast sicher viel für eine sehr ungewöhnliche Plattform umschreiben müssen.

Aber wenn Ihr Code sehr “eigenständig” ist – warum nicht? Sie können es so schreiben, dass verschiedene Byte-Größen möglich sind.

Es scheint, dass Sie immer noch eine IM6100 (dh ein PDP-8 auf einem Chip) aus einem Lager kaufen können. Das ist eine 12-Bit-Architektur.

Viele DSP-Chips haben 16- oder 32-Bit- char . TI stellt routinemäßig solche Chips her.

Die C- und C ++ – Programmiersprachen definieren beispielsweise Byte als “adressierbare Dateneinheit, die groß genug ist, um jedes Element des grundlegenden Zeichensatzes der Ausführungsumgebung aufzunehmen” (Abschnitt 3.6 des C-Standards). Da der Integritätsdatentyp C char mindestens 8 Bits enthalten muss (Abschnitt 5.2.4.2.1), kann ein Byte in C mindestens 256 verschiedene Werte enthalten. Verschiedene Implementierungen von C und C ++ definieren ein Byte als 8, 9, 16, 32 oder 36 Bits

Zitiert von http://en.wikipedia.org/wiki/Byte#History

Nicht sicher über andere Sprachen obwohl.

http://en.wikipedia.org/wiki/IBM_7030_Stretch#Data_Formats

Definiert ein Byte auf dieser Maschine als variable Länge

Die DEC PDP-8-Familie hatte ein 12-Bit-Wort, obwohl Sie normalerweise 8-Bit-ASCII für die Ausgabe verwendeten (meistens bei einem Teletype). Es gab jedoch auch einen 6-BIT-Zeichencode, mit dem Sie 2 Zeichen in einem einzelnen 12-Bit-Wort codieren konnten.

Zum einen sind Unicode-Zeichen länger als 8 Bit. Wie bereits erwähnt, definiert die C-Spezifikation Datentypen nach ihrer Mindestgröße. Verwenden Sie sizeof und die Werte in limits.h wenn Sie Ihre Datentypen abfragen und genau ermitteln möchten, welche Größe sie für Ihre Konfiguration und Architektur haben.

Aus diesem Grund versuche ich bei Datentypen wie uint16_t zu bleiben, wenn ich einen Datentyp einer bestimmten Bitlänge benötige.

Edit: Sorry, ich habe deine Frage zunächst falsch gelesen.

Die C-Spezifikation besagt, dass ein char “groß genug ist, um ein Mitglied des Ausführungszeichensatzes zu speichern”. limits.h listet eine Mindestgröße von 8 Bit auf, aber die Definition lässt die maximale Größe eines char offen.

Daher ist das Zeichen a mindestens so lang wie das größte Zeichen aus dem Ausführungssatz der Architektur (normalerweise aufgerundet auf die nächste 8-Bit-Grenze). Wenn Ihre Architektur längere Opcodes hat, kann die char länger sein.

Historisch war der Opcode der x86-Plattform ein Byte lang, also war char ursprünglich ein 8-Bit-Wert. Derzeitige x86-Plattformen unterstützen Opcodes, die länger als ein Byte sind, aber das Zeichen wird auf 8 Bit Länge gehalten, da dies für Programmierer (und die großen Volumina von existierendem x86-Code) bedingt ist.

Wenn Sie über die Unterstützung stdint.h Plattformen nachdenken, sollten Sie die in stdint.h definierten Typen nutzen. Wenn Sie zum Beispiel ein uint16_t verwenden, können Sie sicher sein, dass dieser Wert ein vorzeichenloser 16-Bit-Wert für jede Architektur ist, unabhängig davon, ob dieser 16-Bit-Wert einem char , short , int oder etwas anderem entspricht. Die meiste harte Arbeit wurde bereits von den Leuten geleistet, die Ihre Compiler / Standard-Bibliotheken geschrieben haben.

Wenn Sie die genaue Größe eines char weil Sie eine Low-Level-Hardware-Manipulation durchführen, die es erfordert, verwende ich typischerweise einen Datentyp, der groß genug ist, um ein Zeichen auf allen unterstützten Plattformen zu speichern (normalerweise sind 16 Bit ausreichend). und führen Sie den Wert durch eine convert_to_machine_char Routine, wenn ich die genaue Maschinendarstellung brauche. Auf diese Weise ist der plattformspezifische Code auf die Schnittstellenfunktion beschränkt und die meiste Zeit kann ich einen normalen uint16_t .

Welche Art von Überlegung ist es wert, Plattformen mit Nicht-8-Bit-Zeichen zu geben?

magische Zahlen treten zB beim Verschieben auf;

Die meisten davon können ganz einfach mit CHAR_BIT und zB UCHAR_MAX anstelle von 8 und 255 (oder ähnlich) gehandhabt werden.

Hoffentlich definiert deine Implementierung diese 🙂

das sind die “gemeinsamen” Themen …..

Ein weiteres indirektes Problem ist, dass Sie haben:

 struct xyz { uchar baz; uchar blah; uchar buzz; } 

dies könnte “nur” (im besten Fall) 24 Bits auf einer Plattform erfordern, könnte aber z. B. 72 Bits anderswo erfordern …..

wenn jeder uchar “bit flags” enthielt und jeder uchar nur 2 “signifikante” bits oder Flags hatte, die Sie gerade benutzten, und Sie diese nur in 3 uchars für “clarity” organisierten, dann könnte es relativ “verschwenderischer” zB auf eine Plattform mit 24-Bit-processoren …..

nichts Bitfields kann nicht lösen, aber sie haben andere Dinge zu beachten ….

In diesem Fall könnte nur eine einzelne Enumeration eine Möglichkeit sein, die “kleinste” ganze Zahl zu erhalten, die Sie tatsächlich benötigen ….

vielleicht kein echtes Beispiel, aber solche Sachen “gebissen” mich beim Portieren / Spielen mit etwas Code …..

nur die Tatsache, dass, wenn ein Uchar dreimal so groß ist wie das, was “normalerweise” erwartet wird, 100 solcher Strukturen eine Menge Speicher auf einigen Plattformen verschwenden … wo “normalerweise” es keine große Sache ist …. .

so können Dinge immer noch “kaputt” sein oder in diesem Fall “sehr schnell sehr viel Speicher verlieren” aufgrund der Annahme, dass ein Uchar “nicht sehr verschwenderisch” auf einer Plattform ist, bezogen auf RAM, als auf einer anderen Plattform … ..

das Problem könnte auch prominenter sein, zB auch für Ints, oder andere Typen, zB haben Sie eine Struktur, die 15 Bits benötigt, also stecken Sie sie in ein int, aber auf einer anderen Plattform ist int 48 Bits oder was auch immer …. .

“normalerweise” könntest du es in 2 uchars brechen, aber zB mit einem 24-bit uchar würdest du nur einen brauchen …..

also könnte ein Enum eine bessere “generische” Lösung sein ….

hängt davon ab, wie Sie auf diese Bits zugreifen, obwohl 🙂

also könnte es “Design-Fehler” geben, die ihren Kopf nach oben ziehen … selbst wenn der Code immer noch funktionieren könnte / gut laufen könnte, unabhängig von der Größe eines Uchar oder Uint …

Es gibt Dinge wie diese, auf die man achten sollte, auch wenn es keine “magischen Zahlen” in deinem Code gibt …

hoffe das macht Sinn 🙂

Ints waren früher 16 Bit (pdp11 usw.). Zu 32-Bit-Architekturen zu gehen war schwierig. Die Leute werden besser: Kaum jemand nimmt an, dass ein pointers nicht mehr lange passt (Sie haben nicht Recht?). Oder Dateioffsets oder Zeitstempel oder …

8-Bit-Zeichen sind schon etwas Anachronismus. Wir benötigen bereits 32 Bits, um alle Zeichensätze der Welt zu speichern.