Datentypen mit fester Länge in C/C++

Lesezeit: 8 Minuten

Ich habe diese Größe von Datentypen wie gehört int kann je nach Plattform variieren.

Meine erste Frage ist: kann jemand ein Beispiel bringen, was schief geht, wenn das Programm einen annimmt int ist 4 Bytes, aber auf einer anderen Plattform sind es sagen wir 2 Bytes?

Eine andere Frage, die ich hatte, ist verwandt. Ich kenne Leute, die dieses Problem mit einigen lösen typedefswie Sie Variablen wie haben u8,u16,u32 – die garantiert 8 Bit, 16 Bit, 32 Bit sind, unabhängig von der Plattform – meine Frage ist, wie wird dies normalerweise erreicht? (Ich beziehe mich nicht auf Typen von stdint Bibliothek – Ich bin neugierig, wie kann man manuell erzwingen, dass ein Typ unabhängig von der Plattform immer 32 Bit sagt??)

  • Es gibt potenzielle Probleme beim Überschreiben des Speichers. Wenn Sie davon ausgehen, dass eine Ganzzahl 4 Byte groß ist, wenn sie auf einer anderen Plattform 2 Byte groß ist, können Sie, je nachdem, wie der Speicher ausgelegt ist, die nächsten 2 Byte nach Ihrer Ganzzahl überschreiben.

    – Austin Brunkhorst

    27. November 2013 um 8:45 Uhr

  • ein guter Zeitpunkt, um die (alte, aber immer noch äußerst informative) C-FAQ aus Usenet-Tagen zu lesen: faqs.org/faqs/C-faq/abridged und dann faqs.org/faqs/C-faq/faq (ungekürzt, also wenn du das stattdessen lesen kannst! viele weitere Infos). Es spricht über viele davon und auch über viele andere oft falsche Annahmen (interne Darstellung von NULL usw.). (Ein Muss ist das Kapitel über Null und über Zeiger/Arrays. Der Rest ist auch GUT und bei vielen Themen aufschlussreich.)

    – Olivier Dulac

    27. November 2013 um 13:48 Uhr


  • Bitte beachten Sie, dass die Byte-Reihenfolge auch von Plattform zu Plattform variieren kann. (+1 für Frage – es ist besser, eine Frage zu stellen, als anzunehmen “sicherlich das sizeof(void *) wird immer sein 4).

    – Maciej Piechotka

    27. November 2013 um 14:48 Uhr

  • @MaciejPiechotka: einverstanden. und es ist gut, diese zu posten, da viele Leser dann auf die potenzielle Falle und ihre Lösungen aufmerksam werden könnten! Es gibt keine schlechten Fragen [well, if they give enough context]nur schlechte Antworten ^^

    – Olivier Dulac

    27. November 2013 um 17:27 Uhr

Benutzeravatar von BЈовић
BЈовић

Ich weiß, dass Leute dieses Problem mit einigen Typedefs lösen, wie Sie Variablen wie u8, u16, u32 haben – die garantiert 8 Bit, 16 Bit, 32 Bit sind, unabhängig von der Plattform

Es gibt einige Plattformen, die keine Typen bestimmter Größe haben (wie zum Beispiel 28xxx von TI, wo die Zeichengröße 16 Bit beträgt). In solchen Fällen ist es nicht möglich, einen 8-Bit-Typ zu haben (es sei denn, Sie möchten es wirklich, aber das kann zu Leistungseinbußen führen).

wie wird das normalerweise erreicht?

Normalerweise mit Typedefs. c99 (und c++11) haben diese Typedefs in einem Header. Also, benutze sie einfach.

Kann jemand ein Beispiel bringen, was schief geht, wenn das Programm davon ausgeht, dass ein Int 4 Bytes groß ist, aber auf einer anderen Plattform 2 Bytes sind?

Bestes Beispiel ist eine Kommunikation zwischen Systemen mit unterschiedlicher Schriftgröße. Beim Senden eines Arrays von Ints von einer zu einer anderen Plattform, bei der sizeof(int) auf zwei unterschiedlich ist, muss man äußerst vorsichtig sein.

Speichern Sie außerdem ein Array von Ints in einer Binärdatei auf einer 32-Bit-Plattform und interpretieren Sie es auf einer 64-Bit-Plattform neu.

  • +1 für Speichern eines Arrays von Ints in einer Binärdatei auf einer 32-Bit-Plattform und Neuinterpretieren auf einer 64-Bit-Plattform..

    – legends2k

    27. November 2013 um 9:27 Uhr

Benutzeravatar von paxdiablo
paxdiablo

In früheren Iterationen des C-Standards haben Sie im Allgemeinen Ihren eigenen erstellt typedef Anweisungen, um sicherzustellen, dass Sie einen (z. B.) 16-Bit-Typ erhalten, basierend auf #define Zeichenfolgen, die zum Beispiel an den Compiler übergeben werden:

gcc -DINT16_IS_LONG ...

Heutzutage (C99 und höher) gibt es bestimmte Typen wie z uint16_tdie genau 16 Bit breite Ganzzahl ohne Vorzeichen.

Vorausgesetzt, Sie schließen ein stdint.herhalten Sie Typen mit exakter Bitbreite, Typen mit mindestens dieser Breite, schnellste Typen mit einer bestimmten Mindestbreite usw., wie in dokumentiert C99 7.18 Integer types <stdint.h>. Wenn eine Implementierung kompatible Typen hat, müssen sie diese bereitstellen.

Auch sehr nützlich ist inttypes.h das einige andere nette Funktionen für die Formatkonvertierung dieser neuen Typen hinzufügt (printf und scanf Formatzeichenfolgen).

  • Unterfrage: Wenn die Plattform keinen 16-Bit-Ganzzahltyp unterstützt unint16_t nicht definiert in cstdint etc..? Oder garantiert der Standard, dass dieser Typ immer vorhanden ist (und intern Dinge tut, um sicherzustellen, dass er funktioniert)?

    – Martin York

    27. November 2013 um 9:18 Uhr


  • Nein, nur der C-Standard erfordert it, wenn die Implementierung einen kompatiblen Typ hat. Wenn Sie beispielsweise einen 12-Bit-DSP verwenden, ist dies nicht der Fall haben um ein 16-Bit-uint16_t bereitzustellen. Es kann ist aber nicht zwingend: 7.18.1.1/3: These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, no padding bits, and (for the signed types) that have a two’s complement representation, it shall define the corresponding typedef names.

    – paxdiablo

    27. November 2013 um 9:20 Uhr


  • Also, wenn Sie verwenden uint16_t und die Plattform dies nicht unterstützt, können wir während des Portierungsvorgangs mit einem Kompilierungsfehler rechnen.

    – Martin York

    27. November 2013 um 9:22 Uhr


  • @Loki, ja, der Compiler kennt den Typ nicht.

    – paxdiablo

    27. November 2013 um 9:24 Uhr

Benutzeravatar von Yu Hao
Yu Hao

Zur ersten Frage: Ganzzahlüberlauf.

Zur zweiten Frage: zum Beispiel to typedef eine vorzeichenlose 32-Bit-Ganzzahl, auf einer Plattform, auf der int ist 4 Bytes, verwenden Sie:

 typedef unsigned int u32;

Auf einer Plattform, wo int ist 2 Bytes während long ist 4 byte:

typedef unsigned long u32;

Auf diese Weise müssen Sie nur eine Header-Datei ändern, um die Typen plattformübergreifend zu machen.

Wenn einige plattformspezifische Makros vorhanden sind, kann dies ohne manuelle Änderung erreicht werden:

#if defined(PLAT1)
typedef unsigned int u32;
#elif defined(PLAT2)
typedef unsigned long u32;
#endif

Wenn C99 stdint.h unterstützt wird, wird es bevorzugt.

  • Macht nichts, es gibt solche Zeiten … – Pause machen!

    – alk

    27. November 2013 um 8:59 Uhr

  • Was ist hier eine Plattform? Ist es die Hardware – wie x86, x86_64, AMD etc… oder ist es ein Betriebssystem – wie Solaris, AIX, HP-UX, Linux, macOS, BSD und IBM z/OS etc…?

    – Darschan L

    14. Oktober 2020 um 12:42 Uhr

Benutzeravatar von Stefan
Stefan

Zunächst einmal: Schreiben Sie niemals Programme, die auf die Breite von Typen angewiesen sind, wie z short, int, unsigned int,….

Grundsätzlich gilt: „Verlasse dich nie auf die Breite, wenn sie nicht durch die Norm gewährleistet ist“.

Wenn Sie wirklich plattformunabhängig sein möchten und zB den Wert 33000 als vorzeichenbehaftete Ganzzahl speichern möchten, können Sie kippen gehen Sie einfach davon aus, dass ein int wird es halten. Ein int hat zumindest die Reichweite -32767 zu 32767 oder -32768 zu 32767 (je nach Einser/Zweier-Komplement). Das ist einfach nicht genug, obwohl es normalerweise ist 32bit und kann daher 33000 speichern. Für diesen Wert braucht man definitiv a >16bit Typ, also wählen Sie einfach aus int32_t oder int64_t. Wenn dieser Typ nicht existiert, teilt Ihnen der Compiler den Fehler mit, aber es handelt sich nicht um einen stillen Fehler.

Zweitens: C++11 bietet einen Standard-Header für Integer-Typen mit fester Breite. Keines davon ist garantiert auf Ihrer Plattform vorhanden, aber wenn es sie gibt, haben sie garantiert die exakte Breite. Sehen diesen Artikel auf cpreference.com für eine Referenz. Die Typen werden im Format benannt int[n]_t und uint[n]_t wo n ist 8, 16, 32 oder 64. Sie müssen den Header einschließen <cstdint>. Das C Kopfzeile ist selbstverständlich <stdint.h>.

Normalerweise tritt das Problem auf, wenn Sie die Anzahl maximieren oder wenn Sie serialisieren. Ein weniger häufiges Szenario tritt auf, wenn jemand eine explizite Größenannahme macht.

Im ersten Szenario:

int x = 32000;
int y = 32000;
int z = x+y;        // can cause overflow for 2 bytes, but not 4

Im zweiten Szenario

struct header {
int magic;
int w;
int h;
};

dann geht man zu fwrite:

header h;
// fill in h
fwrite(&h, sizeof(h), 1, fp);

// this is all fine and good until one freads from an architecture with a different int size

Im dritten Szenario:

int* x = new int[100];
char* buff = (char*)x;


// now try to change the 3rd element of x via buff assuming int size of 2
*((int*)(buff+2*2)) = 100;

// (of course, it's easy to fix this with sizeof(int))

Wenn Sie einen relativ neuen Compiler verwenden, würde ich uint8_t, int8_t usw. verwenden, um die Typgröße sicherzustellen.

In älteren Compilern wird typedef normalerweise pro Plattform definiert. Zum Beispiel kann man Folgendes tun:

 #ifdef _WIN32
      typedef unsigned char uint8_t;
      typedef unsigned short uint16_t;
      // and so on...
 #endif

Auf diese Weise gäbe es einen Header pro Plattform, der die Besonderheiten dieser Plattform definiert.

  • +1 dafür, dass er als erster Strukturen erwähnt hat. Sie sollten sich auch darüber im Klaren sein, was passiert, wenn Sie einen Strct über das Netzwerk senden.

    – James Anderson

    27. November 2013 um 10:28 Uhr

Ich bin neugierig, wie man manuell erzwingen kann, dass ein Typ unabhängig von der Plattform immer 32 Bit sagt?

Wenn Sie möchten, dass die Kompilierung Ihres (modernen) C++-Programms fehlschlägt, wenn ein bestimmter Typ nicht die erwartete Breite hat, fügen Sie a hinzu static_assert irgendwo. Ich würde dies dort hinzufügen, wo die Annahmen über die Breite des Typs getroffen werden.

static_assert(sizeof(int) == 4, "Expected int to be four chars wide but it was not.");

chars auf den am häufigsten verwendeten Plattformen sind 8 Bit groß, aber nicht alle Plattformen funktionieren auf diese Weise.

  • +1 dafür, dass er als erster Strukturen erwähnt hat. Sie sollten sich auch darüber im Klaren sein, was passiert, wenn Sie einen Strct über das Netzwerk senden.

    – James Anderson

    27. November 2013 um 10:28 Uhr

Benutzeravatar von Nemanja Boric
Nemanja Boric

Nun, erstes Beispiel – etwa so:

int a = 45000; // both a and b 
int b = 40000; // does not fit in 2 bytes.
int c = a + b; // overflows on 16bits, but not on 32bits

Wenn Sie hineinschauen cstdint Kopfzeile finden Sie, wie alle Typen mit fester Größe (int8_t, uint8_t, etc.) sind definiert – und der einzige Unterschied zwischen den verschiedenen Architekturen ist diese Header-Datei. Also auf einer Architektur int16_tkönnte sein:

 typedef int int16_t;

und auf einem anderen:

 typedef short int16_t;

Es gibt auch andere Typen, die nützlich sein können, wie: int_least16_t

1417320cookie-checkDatentypen mit fester Länge in C/C++

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

Privacy policy