uint8_t vs unsigned char

231

Was ist der Vorteil der Verwendung von uint8_tmehr als unsigned charin C?

Ich weiß, dass auf fast jedem System uint8_tnur ein Typedef für ist unsigned char, warum also verwenden?

Lyndon White
quelle

Antworten:

225

Es dokumentiert Ihre Absicht - Sie speichern kleine Zahlen anstelle eines Zeichens.

Es sieht auch besser aus, wenn Sie andere Typedefs wie uint16_toder verwenden int32_t.

Mark Ransom
quelle
1
In der ursprünglichen Frage war nicht klar, ob es sich um einen Standardtyp handelte oder nicht. Ich bin sicher, dass es im Laufe der Jahre viele Variationen dieser Namenskonvention gegeben hat.
Mark Ransom
8
Explizite Verwendung unsigned charoder signed charDokumentation der Absicht, da schmucklos charzeigt, dass Sie mit Charakteren arbeiten.
Café
9
Ich dachte , ein schmucklos unsignedwar unsigned intper Definition?
Mark Ransom
5
@endolith, uint8_t für einen String zu verwenden ist nicht unbedingt falsch, aber es ist definitiv komisch.
Mark Ransom
5
@endolith, ich denke, ich kann mit UTF8-Text für uint8_t eintreten. In der Tat charscheint dies ein Zeichen zu implizieren, während es im Kontext einer UTF8-Zeichenfolge nur ein Byte eines Multibyte-Zeichens sein kann. Die Verwendung von uint8_t könnte klarstellen, dass man nicht an jeder Position ein Zeichen erwarten sollte - mit anderen Worten, dass jedes Element des Strings / Arrays eine beliebige Ganzzahl ist, über die man keine semantischen Annahmen treffen sollte. Natürlich wissen das alle C-Programmierer, aber es kann Anfänger dazu bringen, die richtigen Fragen zu stellen.
Der
69

Um pedantisch zu sein, haben einige Systeme möglicherweise keinen 8-Bit-Typ. Laut Wikipedia :

Eine Implementierung ist erforderlich, um ganzzahlige Typen mit exakter Breite für N = 8, 16, 32 oder 64 genau dann zu definieren, wenn ein Typ vorhanden ist, der die Anforderungen erfüllt. Es ist nicht erforderlich, sie für ein anderes N zu definieren, selbst wenn die entsprechenden Typen unterstützt werden.

Es uint8_tist also nicht garantiert, dass es existiert, obwohl dies für alle Plattformen gilt, bei denen 8 Bit = 1 Byte sind. Einige eingebettete Plattformen mögen unterschiedlich sein, aber das wird sehr selten. Einige Systeme definieren charTypen möglicherweise als 16-Bit. In diesem Fall gibt es wahrscheinlich keinen 8-Bit-Typ.

Abgesehen von diesem (kleinen) Problem ist die Antwort von @Mark Ransom meiner Meinung nach die beste. Verwenden Sie diejenige, die am deutlichsten zeigt, wofür Sie die Daten verwenden.

Ich gehe auch davon aus, dass Sie dies gemeint haben uint8_t(das Standard-Typedef von C99 im stdint.hHeader) und nicht uint_8(nicht Teil eines Standards).

Chris Lutz
quelle
3
@caf, aus purer Neugier - kannst du auf die Beschreibung einiger verlinken? Ich weiß, dass sie existieren, weil jemand in einer von comp.lang.c ++ moderierten Diskussion darüber, ob C / C ++ - Typgarantien zu schwach sind, eine erwähnt (und mit Entwicklerdokumenten dafür verlinkt) hat, aber ich kann diesen Thread nicht mehr finden und er ist immer praktisch um darauf in ähnlichen Diskussionen zu verweisen :)
Pavel Minaev
3
"Einige Systeme definieren Zeichentypen möglicherweise als 16-Bit. In diesem Fall gibt es wahrscheinlich keinen 8-Bit-Typ." - und trotz einiger falscher Einwände von mir hat Pavel in seiner Antwort gezeigt, dass wenn char 16 Bit ist, der Compiler, selbst wenn er einen 8-Bit-Typ bereitstellt, ihn nicht aufrufen uint8_t(oder dazu tippen) darf . Dies liegt daran, dass der 8-Bit-Typ nicht verwendete Bits in der Speicherdarstellung enthalten würde, die uint8_tnicht vorhanden sein dürfen.
Steve Jessop
3
Die SHARC-Architektur enthält 32-Bit-Wörter. Siehe en.wikipedia.org/wiki/… für Details.
BCran
2
Die C5000-DSPs von TI (in OMAP1 und OMAP2) sind 16-Bit. Ich denke, für OMAP3 gingen sie mit einem 8-Bit-Char zur C6000-Serie.
Steve Jessop
4
In N3242 - "Arbeitsentwurf, Standard für die Programmiersprache C ++", Abschnitt 18.4.1 <cstdint> Synopsis heißt es: - typedef unsigned integer type uint8_t; // optional Daher ist im Wesentlichen keine C ++ - Standardkonforme Bibliothek erforderlich, um uint8_t überhaupt zu definieren (siehe Kommentar // optional) )
Nightlytrails
43

Der springende Punkt ist, implementierungsunabhängigen Code zu schreiben. unsigned charEs wird nicht garantiert, dass es sich um einen 8-Bit-Typ handelt. uint8_tist (falls verfügbar).

Ameise
quelle
4
... wenn es auf einem System existiert, aber das wird sehr selten sein. +1
Chris Lutz
2
Wenn Sie wirklich Probleme damit hatten, dass Ihr Code nicht auf einem System kompiliert wurde, weil uint8_t nicht vorhanden war, können Sie find und sed verwenden, um alle Vorkommen von uint8_t automatisch in vorzeichenloses Zeichen oder etwas für Sie Nützlicheres zu ändern.
Bazz
2
@bazz - nicht, wenn Sie davon ausgehen, dass es sich um einen 8-Bit-Typ handelt, den Sie nicht können - zum Beispiel, um Daten zu entpacken, die von einem Remote-System byteweise gepackt wurden. Die implizite Annahme ist, dass der Grund dafür, dass uint8_t nicht existiert, auf einem Prozessor liegt, auf dem ein Zeichen mehr als 8 Bit beträgt.
Chris Stratton
Assertion Assert Assert (Sizeof (vorzeichenloses Zeichen) == 8);
Bazz
3
@bazz falsche Behauptung Ich fürchte. sizeof(unsigned char)wird 1für 1 Byte zurückgegeben. aber wenn ein System char und int die gleiche Größe von z. B. 16 Bit haben, sizeof(int)wird auch zurückgegeben1
Toby
7

Wie Sie sagten, " fast jedes System".

charist wahrscheinlich eine der weniger wahrscheinlichen Änderungen, aber sobald Sie anfangen, uint16_tund Freunde zu verwenden, verwenden Sie uint8_tMischungen besser und können sogar Teil eines Codierungsstandards sein.

Nur verliebt
quelle
7

Nach meiner Erfahrung gibt es zwei Stellen, an denen uint8_t 8 Bit (und uint16_t usw.) bedeuten soll und an denen Felder kleiner als 8 Bit sein können. An beiden Stellen kommt es auf den Speicherplatz an, und beim Debuggen müssen wir uns häufig einen Rohdatenauszug der Daten ansehen und schnell feststellen können, was diese darstellen.

Das erste betrifft HF-Protokolle, insbesondere in Schmalbandsystemen. In dieser Umgebung müssen wir möglicherweise so viele Informationen wie möglich in eine einzelne Nachricht packen. Der zweite ist im Flash-Speicher, wo wir möglicherweise nur sehr begrenzten Speicherplatz haben (z. B. in eingebetteten Systemen). In beiden Fällen können wir eine gepackte Datenstruktur verwenden, in der der Compiler das Packen und Entpacken für uns übernimmt:

#pragma pack(1)
typedef struct {
  uint8_t    flag1:1;
  uint8_t    flag2:1;
  padding1   reserved:6;  /* not necessary but makes this struct more readable */
  uint32_t   sequence_no;
  uint8_t    data[8];
  uint32_t   crc32;
} s_mypacket __attribute__((packed));
#pragma pack()

Welche Methode Sie verwenden, hängt von Ihrem Compiler ab. Möglicherweise müssen Sie auch mehrere verschiedene Compiler mit denselben Header-Dateien unterstützen. Dies geschieht in eingebetteten Systemen, in denen Geräte und Server völlig unterschiedlich sein können. Beispielsweise verfügen Sie möglicherweise über ein ARM-Gerät, das mit einem x86-Linux-Server kommuniziert.

Es gibt einige Einschränkungen bei der Verwendung gepackter Strukturen. Das größte Problem ist, dass Sie vermeiden müssen, die Adresse eines Mitglieds zu dereferenzieren. Auf Systemen mit mutibyte-ausgerichteten Wörtern kann dies zu einer falsch ausgerichteten Ausnahme führen - und zu einem Coredump.

Einige Leute werden sich auch Sorgen um die Leistung machen und argumentieren, dass die Verwendung dieser gepackten Strukturen Ihr System verlangsamen wird. Es ist richtig, dass der Compiler hinter den Kulissen Code hinzufügt, um auf die nicht ausgerichteten Datenelemente zuzugreifen. Sie können dies anhand des Assembler-Codes in Ihrer IDE sehen.

Da gepackte Strukturen für die Kommunikation und Datenspeicherung am nützlichsten sind, können die Daten beim Arbeiten damit im Speicher in eine nicht gepackte Darstellung extrahiert werden. Normalerweise müssen wir sowieso nicht mit dem gesamten Datenpaket im Speicher arbeiten.

Hier ist eine relevante Diskussion:

Pragma Pack (1) noch __attribute__ ((ausgerichtet (1))) funktionieren

Ist das __attribute __ ((gepackt)) / #pragma pack von gcc unsicher?

http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html

Tereus Scott
quelle
6

Es gibt wenig. Unter dem Gesichtspunkt der Portabilität charkann nicht kleiner als 8 Bit sein, und nichts kann kleiner sein als char. Wenn also eine bestimmte C-Implementierung einen vorzeichenlosen 8-Bit-Integer-Typ hat, wird dies der Fall sein char. Alternativ kann es sein, dass es zu diesem Zeitpunkt überhaupt keine gibttypedef gibt. Zu Tricks umstritten.

Es könnte verwendet werden, um Ihren Code in dem Sinne besser zu dokumentieren, dass klar ist, dass Sie dort 8-Bit-Bytes und sonst nichts benötigen. In der Praxis ist dies jedoch praktisch überall eine vernünftige Erwartung (es gibt DSP-Plattformen, auf denen dies nicht der Fall ist, aber die Wahrscheinlichkeit, dass Ihr Code dort ausgeführt wird, ist gering, und Sie können genauso gut einen Fehler machen, wenn Sie eine statische Zusicherung oben in Ihrem Programm verwenden eine solche Plattform).

Pavel Minaev
quelle
7
@Skizz - Nein, der Standard muss unsigned charin der Lage sein, Werte zwischen 0 und 255 zu halten. Wenn Sie dies in 4 Bits tun können, ist mein Hut vor Ihnen.
Chris Lutz
1
"Es wäre etwas umständlicher" - umständlich in dem Sinne, dass Sie bis zu dem Ort, an dem sich der Compiler-Autor befand, laufen (schwimmen, ein Flugzeug nehmen usw.) und sie in den Hinterkopf schlagen müssten und lassen Sie sie uint8_tzur Implementierung hinzufügen . Ich frage mich, ob Compiler für DSPs mit 16-Bit-Zeichen normalerweise implementiert uint8_twerden oder nicht.
Steve Jessop
6
Übrigens, bei einem zweiten Gedanken ist es vielleicht die einfachste Art zu sagen "Ich brauche wirklich 8 Bits" - #include <stdint.h>und zu verwenden uint8_t. Wenn die Plattform es hat, wird es Ihnen geben. Wenn die Plattform es nicht hat, wird Ihr Programm nicht kompiliert und der Grund ist klar und unkompliziert.
Pavel Minaev
2
Immer noch keine Zigarre, sorry: "Bei anderen vorzeichenlosen Ganzzahltypen als vorzeichenlosen Zeichen werden die Bits der Objektdarstellung in zwei Gruppen unterteilt: Wertbits und Füllbits ... Wenn N Wertbits vorhanden sind, muss jedes Bit ein anderes darstellen Potenz von 2 zwischen 1 und 2 ^ (N-1), so dass Objekte dieses Typs Werte von 0 bis 2 ^ (N-1) unter Verwendung einer reinen Binärdarstellung darstellen können ... Der typedef-Name intN_t bezeichnet a Ganzzahl mit Vorzeichen, Breite N, keine Füllbits und Zweierkomplementdarstellung. "
Pavel Minaev
1
Wenn Sie nur arithmetisches Modulo benötigen, reicht ein vorzeichenloses Bitfeld aus (wenn es unpraktisch ist). Wenn Sie beispielsweise eine Reihe von Oktetten ohne Polsterung benötigen, sind Sie SOL. Die Moral der Geschichte ist nicht, für DSPs zu codieren und sich an die richtigen, ehrlichen 8-Bit-Char-Architekturen zu halten :)
Pavel Minaev,
4

Das ist zum Beispiel sehr wichtig, wenn Sie einen Netzwerkanalysator schreiben. Paket-Header werden durch die Protokollspezifikation definiert, nicht durch die Funktionsweise des C-Compilers einer bestimmten Plattform.

VP.
quelle
Als ich das fragte, war ich definitiv ein einfaches Protokoll für die Kommunikation über serielle.
Lyndon White
2

Auf fast jedem System habe ich uint8_t == unsigned char getroffen, aber dies wird vom C-Standard nicht garantiert. Wenn Sie versuchen, tragbaren Code zu schreiben und es genau darauf ankommt, wie groß der Speicher ist, verwenden Sie uint8_t. Verwenden Sie andernfalls ein Zeichen ohne Vorzeichen.

atlpeg
quelle
3
uint8_t Entspricht immer dem Bereich und der Größe von unsigned charund dem Auffüllen (keine), wenn unsigned char es sich um 8-Bit handelt. Wenn unsigned charnicht 8-Bit ist, uint8_texistiert nicht.
chux
@chux, Haben Sie einen Verweis auf die genaue Stelle im Standard, an der das steht? Wenn unsigned chares sich um 8-Bit handelt, handelt es sich uint8_tgarantiert um eine typedefdavon und nicht typedefum eine erweiterte vorzeichenlose Ganzzahl ?
Hsivonen
@hsivonen "genaue Stelle im Standard, wo es das sagt?" -> Nein - noch zu 7.20.1.1 schauen. Es lässt sich leicht ableiten, ebenso unsigned char/signed char/charwie der kleinste Typ - nicht kleiner als 8 Bit. unsigned charhat keine Polsterung. Um uint8_tzu sein, muss es 8-Bit sein, keine Auffüllung, da aufgrund einer Implementierung ein ganzzahliger Typ bereitgestellt wird: der den Mindestanforderungen von entspricht unsigned char. "... garantiert ein typedef ..." scheint eine gute Frage zu sein.
chux