Ich habe gehört, dass die Größe der Datentypen int
je nach Plattform variieren kann.
Meine erste Frage ist: Kann jemand ein Beispiel bringen, was schief geht, wenn das Programm int
4 Bytes annimmt , auf einer anderen Plattform jedoch 2 Bytes?
Eine andere Frage, die ich hatte, ist verwandt. Ich kenne Leute , dieses Problem mit einigen lösen typedefs
, wie Sie Variablen wie haben u8
, u16
, u32
- die 8bits, 16bit, 32bit sein, unabhängig von der Plattform garantiert werden - meine Frage ist, wie dies in der Regel erreicht wird? (Ich beziehe mich nicht auf Typen aus der stdint
Bibliothek - ich bin manuell neugierig, wie kann man erzwingen, dass ein Typ unabhängig von der Plattform immer 32 Bit sagt?)
sizeof(void *)
wird das immer so sein4
).Antworten:
Es gibt einige Plattformen, die keine Typen bestimmter Größe haben (wie zum Beispiel 28xxx von TI, wo die Größe von char 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 dies kann zu Leistungseinbußen führen).
Normalerweise mit typedefs. c99 (und c ++ 11) haben diese Typedefs in einem Header . Also benutze sie einfach.
Das beste Beispiel ist eine Kommunikation zwischen Systemen mit unterschiedlicher Typgröße. Wenn Sie eine Reihe von Ints von einer zu einer anderen Plattform senden, bei der sizeof (int) bei zwei unterschiedlich ist, muss Sie äußerst vorsichtig sein.
Speichern Sie außerdem eine Reihe von Ints in einer Binärdatei auf einer 32-Bit-Plattform und interpretieren Sie sie auf einer 64-Bit-Plattform neu.
quelle
In früheren Iterationen des C-Standards haben Sie im Allgemeinen Ihre eigenen
typedef
Anweisungen gemacht, um sicherzustellen, dass Sie einen (zum Beispiel) 16-Bit-Typ erhalten, der auf#define
Zeichenfolgen basiert, die beispielsweise an den Compiler übergeben wurden:Heutzutage (C99 und höher) gibt es bestimmte Typen wie
uint16_t
die genau 16 Bit breite Ganzzahl ohne Vorzeichen.Vorausgesetzt, Sie geben an
stdint.h
, erhalten Sie genaue Bitbreitentypen, Typen mit mindestens dieser Breite, schnellste Typen mit einer bestimmten Mindestbreite usw., wie in dokumentiertC99 7.18 Integer types <stdint.h>
. Wenn eine Implementierung kompatible Typen hat, müssen diese bereitgestellt werden.Sehr nützlich ist auch
inttypes.h
das Hinzufügen einiger weiterer nützlicher Funktionen für die Formatkonvertierung dieser neuen Typen (printf
undscanf
Formatzeichenfolgen).quelle
unint16_t
incstdint
etc .. nicht definiert ? Oder garantiert der Standard, dass der Typ immer vorhanden ist (und intern Dinge erledigt, um sicherzustellen, dass er funktioniert)?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.
uint16_t
und die Plattform dies nicht unterstützt, können wir während der Portierungsprozesse einen Kompilierungsfehler erwarten.Zur ersten Frage: Integer Overflow .
Für die zweite Frage: Verwenden Sie beispielsweise für
typedef
eine vorzeichenlose 32-Bit-Ganzzahl auf einer Plattform mitint
4 Byte Folgendes:typedef unsigned int u32;
Auf einer Plattform mit
int
2 Bytes undlong
4 Bytes:typedef unsigned long u32;
Auf diese Weise müssen Sie nur eine Header-Datei ändern, um die Typen plattformübergreifend zu gestalten.
Wenn es einige plattformspezifische Makros gibt, 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.quelle
Zunächst einmal: Nie Schreibprogramme , die auf die Breite der Typen verlassen möchten
short
,int
,unsigned int
, ....Grundsätzlich gilt: "Verlassen Sie sich niemals auf die Breite, wenn dies nicht durch den Standard garantiert wird".
Wenn Sie wirklich plattformunabhängig sein und beispielsweise den Wert 33000 als vorzeichenbehaftete Ganzzahl speichern möchten, können Sie nicht einfach davon ausgehen, dass ein
int
diesen Wert enthält . Anint
hat mindestens den Bereich-32767
bis32767
oder-32768
bis32767
(abhängig von Einsen / Zweien-Komplement). Das ist einfach nicht genug, obwohl es normalerweise 32 Bit ist und daher 33000 speichern kann. Für diesen Wert benötigen Sie definitiv einen>16bit
Typ, daher wählen Sie einfachint32_t
oderint64_t
. Wenn dieser Typ nicht vorhanden ist, teilt Ihnen der Compiler den Fehler mit, es handelt sich jedoch nicht um einen stillen Fehler.Zweitens: C ++ 11 bietet einen Standardheader für Ganzzahltypen mit fester Breite. Keines davon ist garantiert auf Ihrer Plattform vorhanden, aber wenn es vorhanden ist, hat es garantiert die exakte Breite. In diesem Artikel auf cppreference.com finden Sie eine Referenz. Die Typen werden in dem Format genannt
int[n]_t
unduint[n]_t
won
ist8
,16
,32
oder64
. Sie müssen den Header einfügen<cstdint>
. DerC
Header ist natürlich<stdint.h>
.quelle
uint32_t
dass sie 32 Bit breit sind? Abstraktionen sind nett und alles, aber irgendwann kommt ein Punkt, an dem Sie einige Annahmen treffen müssen, um tatsächlich etwas zu erledigen.Normalerweise tritt das Problem auf, wenn Sie die Anzahl maximal nutzen 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 zum 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 zu gewährleisten.
In älteren Compilern wird typedef normalerweise pro Plattform definiert. Zum Beispiel kann man tun:
#ifdef _WIN32 typedef unsigned char uint8_t; typedef unsigned short uint16_t; // and so on... #endif
Auf diese Weise würde es einen Header pro Plattform geben, der die Besonderheiten dieser Plattform definiert.
quelle
Wenn die Kompilierung Ihres (modernen) C ++ - Programms fehlschlagen soll, wenn ein bestimmter Typ nicht die erwartete Breite hat, fügen Sie
static_assert
irgendwo einen hinzu. Ich würde dies hinzufügen, wenn 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.quelle
sizeof
Gibt tatsächlich die Größe inchar
s zurück, nicht in Bytes. Wenn Sie also die Größe in Bit überprüfen möchten , sollten Sie dies tunsizeof(int) * CHAR_BIT == 32
.sizeof(char)==1
- immer.uint32_t
usw. wurden gleichzeitig mit hinzugefügtstatic_assert
.char
hat immerCHAR_BIT
Bits.CHAR_BIT
ist mindestens 8, kann aber mehr sein.Nun, erstes Beispiel - so etwas:
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 in schauen
cstdint
Header, werden Sie feststellen , wie alle feste Größe Typen (int8_t
,uint8_t
usw.) definiert sind - und einzige , was unterscheidet zwischen verschiedenen Architekturen sind diese Header - Datei. Eine Architekturint16_t
könnte also 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
quelle
typedef
s basierend auf#ifdef
den jeweiligen Plattformen.quelle
Angenommen, Sie haben Ihr Programm so konzipiert, dass es 100.000 Eingaben liest, und Sie zählen es mit einer
unsigned int
angenommenen Größe von 32 Bit (32-Bit-Ints ohne Vorzeichen können bis zu 4.294.967.295 zählen). Wenn Sie den Code auf einer Plattform (oder einem Compiler) mit 16-Bit-Ganzzahlen kompilieren (16-Bit-Ints ohne Vorzeichen können nur bis 65.535 zählen), wird der Wert aufgrund der Kapazität über 65535 hinausgehen und eine falsche Anzahl anzeigen.quelle
Compiler sind dafür verantwortlich, den Standard einzuhalten. Wenn Sie einschließen
<cstdint>
oder<stdint.h>
sie Typen gemäß Standardgröße bereitstellen.Compiler wissen, dass sie den Code für welche Plattform kompilieren, und können dann einige interne Makros oder Magics generieren, um den geeigneten Typ zu erstellen. Ein Compiler auf einem 32-Bit-Computer generiert beispielsweise ein
__32BIT__
Makro und hat zuvor folgende Zeilen in derstdint
Header-Datei:#ifdef __32BIT__ typedef __int32_internal__ int32_t; typedef __int64_internal__ int64_t; ... #endif
und du kannst es benutzen.
quelle
Bitflags sind das triviale Beispiel. 0x10000 verursacht Probleme, Sie können nicht damit maskieren oder prüfen, ob ein Bit an dieser 17. Position gesetzt ist, wenn alles abgeschnitten oder zerschlagen wird, um in 16-Bit zu passen.
quelle