Unterschied zwischen uint8_t, uint_fast8_t und uint_least8_t

Lesezeit: 10 Minuten

Benutzeravatar von mic
Mikrofon

Der C99-Standard führt die folgenden Datentypen ein. Die Dokumentation ist zu finden hier für die AVR stdint Bibliothek.

  • uint8_t bedeutet, dass es sich um einen 8-Bit-Typ ohne Vorzeichen handelt.
  • uint_fast8_t bedeutet, dass es das schnellste unsigned int mit mindestens 8 Bit ist.
  • uint_least8_t bedeutet, dass es sich um ein unsigned int mit mindestens 8 Bit handelt.

Ich verstehe uint8_t und was ist uint_fast8_t(Ich weiß nicht, wie es auf Registerebene implementiert ist).

1. Können Sie erklären, was „es ist ein unsigned int mit mindestens 8 Bit”?

2.Wie uint_fast8_t und uint_least8_t helfen, die Effizienz/den Coderaum im Vergleich zu zu erhöhen uint8_t?

  • Für Ihre 1. Frage kann ich mir das vorstellen uint8_t ist garantiert 8 Bit, uint_fast8_t ist garantiert >= 8 Bit, ähnlich wie ein unsigned char.

    – Jacob

    28. Januar 2016 um 7:24 Uhr

  • Eine Überlegung ist das uint8_t existiert nicht auf Systemen, die keinen nativen 8-Bit-Typ haben. Die anderen beiden werden dabei sein.

    – Peter Becker

    28. Januar 2016 um 14:04 Uhr

  • Sie haben Antworten erhalten, die sich auf „obskure“ und „exotische“ Architekturen beziehen. Diese Begriffe sind etwas voreingenommen. Sicher, wenn Sie nur Erfahrung mit Desktop-Systemen haben, liegen diese Architekturen außerhalb Ihres Erfahrungsbereichs. Aber „das habe ich noch nie gesehen“ ist nicht dasselbe wie „das ist obskur oder exotisch“. Für Leute, die mit eingebetteten Systemen oder DSPs arbeiten, sind diese Dinge durchaus üblich.

    – Peter Becker

    28. Januar 2016 um 14:08 Uhr

  • Mögliches Duplikat von Die Differenz von int8_t, int_least8_t und int_fast8_t?

    – dan04

    28. Januar 2016 um 23:24 Uhr

rodrigos Benutzeravatar
rodrigo

uint_least8_t ist der kleinste Typ, der mindestens 8 Bits hat.
uint_fast8_t ist der schnellste Typ, der mindestens 8 Bits hat.

Sie können die Unterschiede erkennen, indem Sie sich exotische Architekturen vorstellen. Stellen Sie sich eine 20-Bit-Architektur vor. Es ist unsigned int hat 20 Bit (ein Register) und seine unsigned char hat 10 bit. So sizeof(int) == 2aber mit char -Typen erfordert zusätzliche Anweisungen, um die Register zu halbieren. Dann:

  • uint8_t: ist undefiniert (kein 8-Bit-Typ).
  • uint_least8_t: ist unsigned charder kleinste Typ mit mindestens 8 Bit.
  • uint_fast8_t: ist unsigned intweil in meiner imaginären Architektur eine Halbregistervariable langsamer ist als eine Vollregistervariable.

  • Ich finde es toll, wie man sich exotische Architekturen vorstellen muss, um einen Anwendungsfall dafür zu finden. Haben sie in der Praxis einen Nutzen gefunden?

    – Benutzer541686

    28. Januar 2016 um 7:28 Uhr

  • @Mehrdad In ARM zum Beispiel, wenn Ihr int_fast8_t eine 32-Bit-Variable ist, müssen Sie vor arithmetischen Operationen keine Vorzeichenerweiterung durchführen.

    – Benutzer694733

    28. Januar 2016 um 7:30 Uhr


  • @Mehrdad MIPS zum Beispiel wäre es sehr falsch, welche zu machen uintX_fast_t weniger als 32 Bit. Sie müssen sich nicht einmal Architekturen vorstellen, um sie zu bekommen uint8_t undefiniert zu sein, nehmen Sie zum Beispiel UNIVAC, das 36-Bit ist, würde ich davon ausgehen, dass es dort ist char ist 9bit.

    – Himmel König

    28. Januar 2016 um 8:01 Uhr

  • @Mehrdad: Ich gebe zu, dass ich das noch nie gesehen habe uint_leastX_t oder uint_fastX_t in realen Anwendungen verwendet. uintX_t ja, sie werden stark genutzt. Es sieht so aus, als wären die Leute nicht sehr interessiert an der Portabilität auf exotische Architekturen. Was zu erwarten ist, selbst wenn Sie Ihre unsigneds richtig machen, wird Ihr Programm bei tausend verschiedenen Dingen scheitern.

    – rodrigo

    28. Januar 2016 um 8:36 Uhr

  • @skyking: Ich sage nicht, dass sie nicht verwendet werden sollten, nur dass sie in der Praxis nicht sehr häufig verwendet werden. Wenn Sie eine reale Anwendung oder Bibliothek finden, die sie sinnvoll verwendet, posten Sie einen Link, da ich keine finden konnte.

    – rodrigo

    28. Januar 2016 um 8:46 Uhr

uint8_t bedeutet: gib mir ein unsigned int von genau 8 Bits.

uint_least8_t bedeutet: gib mir die kleinste Art von unsigned int, die mindestens 8 Bit hat. Optimieren Sie den Speicherverbrauch.

uint_fast8_t bedeutet: gib mir einen unsigned int von mindestens 8 bit. Wählen Sie aus Gründen der Ausrichtung einen größeren Typ, wenn dadurch mein Programm schneller wird. Auf Geschwindigkeit optimieren.

Auch im Gegensatz zur Ebene int -Typen ist die signierte Version der obigen stdint.h-Typen garantiert im 2er-Komplement-Format.

  • Vielen Dank. Gut zu wissen, dass der Signierte eintippt stdint.h sind garantiert Zweierkomplement. Frage mich, wo es beim Schreiben von portablem Code helfen wird.

    – legends2k

    28. Januar 2016 um 7:46 Uhr


  • Beachten Sie, dass nur die genauen Breitenvarianten erforderlich sind, um das Zweierkomplementformat zu verwenden. Beachten Sie auch, dass diese nicht vorhanden sein müssen. Folglich ist keine Plattform erforderlich, um das Zweierkomplementformat zu unterstützen.

    – Himmel König

    28. Januar 2016 um 7:52 Uhr

  • @legends2k: Die Typen in stdint.h sind eher weniger hilfreich, als man möchte, wenn man versucht, portablen Code zu schreiben, da sie zwar das Zweierkomplement-Speicherformat verwenden müssen, dies jedoch nicht bedeutet, dass sie ein Zweierkomplement-Wrapping-Verhalten aufweisen. Beachten Sie auch das sogar auf Plattformen, wo int ist 32 BitSchreiben eines Werts mit einem int32_t* und Lesen mit einem int*oder umgekehrt, funktioniert nicht garantiert.

    – Superkatze

    29. Januar 2016 um 17:29 Uhr


  • @supercat Jeder Compiler, den ich gesehen habe, verwendet eine interne Typedef für die stdint.h-Typen, um sie synonym mit einem der grundlegenden “Schlüsselwort”-Integer-Typen zu machen. Wenn Sie sich also Gedanken über Zeiger-Aliasing machen, glaube ich nicht, dass dies in der Praxis ein Problem sein wird, sondern nur in der Theorie.

    – Ludin

    1. Februar 2016 um 7:19 Uhr

  • @Lundin: Einige Compiler verwenden “long” als Typedef für int32_t und andere verwenden “int”. Selbst wenn “int” und “long” die gleiche Darstellung haben, können sie (und werden manchmal) als unterschiedlich für die Zwecke der Aliasing-Regeln von C betrachtet.

    – Superkatze

    1. Februar 2016 um 14:43 Uhr

Benutzeravatar von Plugwash
Plugwash

Die Theorie geht ungefähr so:

uint8_t muss genau 8 Bit sein, muss aber nicht existieren. Sie sollten es also dort verwenden, wo Sie sich auf das Modulo-256-Zuweisungsverhalten * einer 8-Bit-Ganzzahl verlassen und wo Sie einen Kompilierungsfehler einem Fehlverhalten auf obskuren Architekturen vorziehen würden.

uint_least8_t muss der kleinste verfügbare vorzeichenlose Ganzzahltyp sein, der mindestens 8 Bit speichern kann. Sie würden es verwenden, wenn Sie den Speicherverbrauch von Dingen wie großen Arrays minimieren möchten.

uint_fast8_t soll der “schnellste” vorzeichenlose Typ sein, der mindestens 8 Bit speichern kann; Es ist jedoch nicht garantiert, dass es für einen bestimmten Vorgang auf einem bestimmten Prozessor der schnellste ist. Sie würden es in Verarbeitungscode verwenden, der viele Operationen mit dem Wert ausführt.

Die Praxis ist, dass die Typen “schnell” und “am wenigsten” nicht oft verwendet werden.

Die “geringsten” Typen sind nur dann wirklich nützlich, wenn Sie sich um die Portabilität zu obskuren Architekturen mit CHAR_BIT != 8 kümmern, was die meisten Leute nicht tun.

Das Problem mit den „schnellen“ Typen ist, dass „am schnellsten“ schwer festzumachen ist. Ein kleinerer Typ kann eine geringere Belastung des Speicher-/Cache-Systems bedeuten, aber die Verwendung eines Typs, der kleiner als der native ist, kann zusätzliche Anweisungen erfordern. Darüber hinaus kann sich die beste Lösung zwischen Architekturversionen ändern, aber Implementierer möchten in solchen Fällen häufig vermeiden, ABI zu brechen.

Wenn man sich einige populäre Implementierungen ansieht, scheint es, dass die Definitionen von uint_fastn_t sind ziemlich willkürlich. glibc scheint sie als mindestens die “native Wortgröße” des betreffenden Systems zu definieren, ohne die Tatsache zu berücksichtigen, dass viele moderne Prozessoren (insbesondere 64-Bit-Prozessoren) spezielle Unterstützung für schnelle Operationen auf Elementen haben, die kleiner als ihr natives Wort sind Größe. IOS definiert sie anscheinend als äquivalent zu den Typen mit fester Größe. Andere Plattformen können abweichen.

Alles in allem, wenn die Leistung von engem Code mit winzigen Ganzzahlen Ihr Ziel ist, sollten Sie ein Benchmarking durchführen dein Code auf den Plattformen, die Ihnen wichtig sind, mit Typen unterschiedlicher Größe, um zu sehen, was am besten funktioniert.

* Beachten Sie, dass das Modulo-256-Zuweisungsverhalten leider nicht immer Modulo-256-Arithmetik impliziert, dank der Integer-Promotion-Fehlfunktion von C.

  • Die Definitionen von glibc wurden zu einer Zeit gewählt, als diese Optimierungen noch nicht existierten, und sie sind jetzt in die ABI eingebrannt und können nicht geändert werden. Dies ist einer der Gründe, warum die Typen _least und _fast in der Praxis nicht wirklich nützlich sind.

    – zol

    28. Januar 2016 um 17:19 Uhr

  • @zwol: Ich wünschte, die Sprache würde Typentypen hinzufügen, die in Bezug auf Layout und semantische Anforderungen definiert wurden, z es ist nicht nötig, größere Werte an diesen Bereich zu binden”. Aliasing, Layout, Bereich und Out-of-Range-Verhalten sollten vier separate Aspekte eines Typs sein, aber C erlaubt nur bestimmte Kombinationen, die auf verschiedenen Plattformen nicht konsistent sind.

    – Superkatze

    28. Januar 2016 um 23:45 Uhr

Einige Prozessoren können mit kleineren Datentypen nicht so effizient arbeiten wie mit großen. Zum Beispiel gegeben:

uint32_t foo(uint32_t x, uint8_t y)
{
  x+=y;
  y+=2;
  x+=y;
  y+=4;
  x+=y;
  y+=6;
  x+=y;
  return x;
}

wenn y war uint32_t ein Compiler für den ARM Cortex-M3 könnte einfach generiert werden

add r0,r0,r1,asl #2   ; x+=(y<<2)
add r0,r0,#12         ; x+=12
bx  lr                ; return x

aber seit y ist uint8_t Der Compiler müsste stattdessen Folgendes generieren:

add r0,r0,r1          ; x+=y
add r1,r1,#2          ; Compute y+2
and r1,r1,#255        ; y=(y+2) & 255
add r0,r0,r1          ; x+=y
add r1,r1,#4          ; Compute y+4
and r1,r1,#255        ; y=(y+4) & 255
add r0,r0,r1          ; x+=y
add r1,r1,#6          ; Compute y+6
and r1,r1,#255        ; y=(y+6) & 255
add r0,r0,r1          ; x+=y
bx  lr                ; return x

Der beabsichtigte Zweck der “schnellen” Typen war es, Compilern zu ermöglichen, kleinere Typen, die nicht effizient verarbeitet werden konnten, durch schnellere zu ersetzen. Leider ist die Semantik von “schnellen” Typen ziemlich schlecht spezifiziert, was wiederum unklare Fragen darüber hinterlässt, ob Ausdrücke mit vorzeichenbehafteter oder vorzeichenloser Mathematik ausgewertet werden.

1. Können Sie erklären, was “es ist ein unsigned int mit mindestens 8 Bits” bedeutet?

Das sollte klar sein. Dies bedeutet, dass es sich um einen vorzeichenlosen Integer-Typ handelt und dass seine Breite mindestens 8 Bit beträgt. In der Tat bedeutet dies, dass es zumindest die Zahlen 0 bis 255 enthalten kann, und es kann definitiv keine negativen Zahlen enthalten, aber es kann in der Lage sein, Zahlen über 255 zu speichern.

Natürlich sollten Sie keinen dieser Typen verwenden, wenn Sie vorhaben, eine Zahl außerhalb des Bereichs von 0 bis 255 zu speichern (und Sie möchten, dass sie portierbar ist).

2.Wie tragen uint_fast8_t und uint_least8_t dazu bei, die Effizienz/den Coderaum im Vergleich zu uint8_t zu erhöhen?

uint_fast8_t erforderlich ist, um schneller zu sein, also sollten Sie dies verwenden, wenn Ihre Anforderung darin besteht, dass der Code schnell ist. uint_least8_t auf der anderen Seite erfordert, dass es keinen Kandidaten mit geringerer Größe gibt – also würden Sie das verwenden, wenn es um die Größe geht.


Und natürlich verwenden Sie nur uint8_t wenn es unbedingt genau 8 Bit sein müssen. Verwenden uint8_t kann den Code als nicht portierbar machen uint8_t muss nicht vorhanden sein (weil ein solcher kleiner ganzzahliger Typ auf bestimmten Plattformen nicht vorhanden ist).

Die “schnellen” Ganzzahltypen sind definiert als die schnellste verfügbare Ganzzahl mit mindestens der erforderlichen Anzahl von Bits (in Ihrem Fall 8).

Eine Plattform kann definieren uint_fast8_t wie uint8_t Dann gibt es absolut keinen Geschwindigkeitsunterschied.

Der Grund dafür ist, dass es Plattformen gibt, die langsamer sind, wenn sie nicht ihre native Wortlänge verwenden.

vjalles Benutzeravatar
vjalle

Ich verwende die schnellen Datentypen (uint_fast8_t) für lokale Variablen und Funktionsparameter und die normalen (uint8_t) in Arrays und Strukturen, die häufig verwendet werden, und der Speicherbedarf ist wichtiger als die wenigen Zyklen, die durch Nichtvorhandensein eingespart werden könnten zum Löschen oder Signieren erweitern Sie die oberen Bits. Funktioniert super, außer mit MISRA Checkers. Sie werden verrückt von den schnellen Typen. Der Trick besteht darin, dass die schnellen Typen durch abgeleitete Typen verwendet werden, die für MISRA-Builds anders definiert werden können als für normale.

Ich denke, diese Typen eignen sich hervorragend zum Erstellen von portablem Code, der sowohl auf Low-End-Mikrocontrollern als auch auf großen Anwendungsprozessoren effizient ist. Die Verbesserung ist vielleicht nicht riesig oder bei guten Compilern völlig vernachlässigbar, aber besser als nichts.

1420120cookie-checkUnterschied zwischen uint8_t, uint_fast8_t und uint_least8_t

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy