unsigned int vs. size_t

492

Ich stelle fest, dass moderner C- und C ++ - Code size_tanstelle von int/ unsigned intfast überall verwendet wird - von Parametern für C-String-Funktionen bis hin zur STL. Ich bin gespannt auf den Grund dafür und die damit verbundenen Vorteile.

rauben
quelle

Antworten:

388

Der size_tTyp ist der vorzeichenlose Integer-Typ, der das Ergebnis des sizeofOperators (und des offsetofOperators) ist. Er ist also garantiert groß genug, um die Größe des größten Objekts aufzunehmen, das Ihr System verarbeiten kann (z. B. ein statisches Array von 8 GB).

Der size_tTyp kann größer, gleich oder kleiner als ein sein unsigned int, und Ihr Compiler kann zur Optimierung Annahmen darüber treffen.

Genauere Informationen finden Sie im C99-Standard, Abschnitt 7.17, dessen Entwurf im PDF- Format im Internet verfügbar ist , oder im C11-Standard, Abschnitt 7.19, der auch als PDF-Entwurf verfügbar ist .

Remo.D
quelle
50
Nee. Denken Sie an x86-16 mit dem großen (nicht großen) Speichermodell: Zeiger sind weit (32-Bit), aber einzelne Objekte sind auf 64 KB beschränkt (also kann size_t 16-Bit sein).
Dan04
8
"Größe des größten Objekts" ist keine schlechte Formulierung, aber absolut korrekt. Die Sechser eines Objekts können viel begrenzter sein als der Adressraum.
Gnasher729
3
"Ihr Compiler könnte davon ausgehen": Ich hoffe, der Compiler kennt den genauen Wertebereich, der dargestellt werden size_tkann! Wenn nicht, wer dann?
Marc van Leeuwen
4
@Marc: Ich denke, es ging eher darum, dass der Compiler mit diesem Wissen etwas anfangen kann.
8
Ich wünschte nur, dieser immer beliebter werdende Typ würde nicht die Aufnahme einer Header-Datei erfordern.
user2023370
98

Classic C (der frühe Dialekt von C, beschrieben von Brian Kernighan und Dennis Ritchie in The C Programming Language, Prentice-Hall, 1978) lieferte dies nicht size_t. Das C-Standardkomitee wurde eingeführt size_t, um ein Portabilitätsproblem zu beseitigen

Ausführlich erklärt bei embedded.com (mit einem sehr guten Beispiel)

Azeemarif
quelle
6
Ein weiterer großartiger Artikel, der sowohl size_t als auch ptrdiff_t erklärt: viva64.com/en/a/0050
Ihor Kaharlichenko
73

Kurz gesagt, size_tist niemals negativ und maximiert die Leistung, da es sich um einen vorzeichenlosen Integer-Typ handelt, der groß genug - aber nicht zu groß - ist, um die Größe des größtmöglichen Objekts auf der Zielplattform darzustellen.

Größen sollten niemals negativ sein und sind in der Tat size_tein vorzeichenloser Typ. Da size_tes kein Vorzeichen gibt, können Sie auch Zahlen speichern, die ungefähr doppelt so groß sind wie der entsprechende vorzeichenbehaftete Typ, da wir das Vorzeichenbit verwenden können, um die Größe darzustellen, wie alle anderen Bits in der vorzeichenlosen Ganzzahl. Wenn wir noch ein Bit gewinnen, multiplizieren wir den Bereich der Zahlen, den wir darstellen können, mit einem Faktor von ungefähr zwei.

Sie fragen sich also, warum nicht einfach eine verwenden unsigned int? Es ist möglicherweise nicht in der Lage, ausreichend große Zahlen zu halten. In einer Implementierung mit unsigned int32 Bit kann die größte Zahl dargestellt werden 4294967295. Einige Prozessoren, wie z. B. IP16L32, können Objekte kopieren, die größer als 4294967295Bytes sind.

Also, fragen Sie, warum nicht eine verwenden unsigned long int? Auf einigen Plattformen ist eine Leistungsbeeinträchtigung erforderlich. Standard C erfordert, dass ein longmindestens 32 Bit belegt. Eine IP16L32-Plattform implementiert jede 32-Bit-Länge als ein Paar von 16-Bit-Wörtern. Fast alle 32-Bit-Operatoren auf diesen Plattformen benötigen zwei Anweisungen, wenn nicht mehr, da sie mit den 32-Bit-Operatoren in zwei 16-Bit-Blöcken arbeiten. Zum Beispiel erfordert das Verschieben eines 32-Bit-Longs normalerweise zwei Maschinenbefehle - einen zum Verschieben jedes 16-Bit-Chunks.

Durch size_tdie Verwendung wird diese Leistungsbelastung vermieden. In diesem fantastischen Artikel heißt es: "Type size_tist ein Typedef, der ein Alias ​​für einen vorzeichenlosen Integer-Typ ist, normalerweise unsigned intoder unsigned longaber möglicherweise sogar unsigned long long. Jede Standard C-Implementierung soll die vorzeichenlose Ganzzahl auswählen, die groß genug ist - aber nicht größer als erforderlich - um die Größe des größtmöglichen Objekts auf der Zielplattform darzustellen. "

Rose Perrone
quelle
1
Es tut mir leid, dies nach so langer Zeit zu kommentieren, aber ich musste nur die größte Zahl bestätigen, die ein Int ohne Vorzeichen enthalten kann - vielleicht verstehe ich Ihre Terminologie falsch, aber ich dachte, dass die größte Zahl, die ein Int ohne Vorzeichen enthalten kann, 4294967295 ist, wobei 65356 ist das Maximum eines vorzeichenlosen Kurzschlusses.
Mitch
Wenn Ihr vorzeichenloses int 32 Bit belegt, ist die größte Zahl, die es enthalten kann, 2 ^ 32 - 1, also 4294967295 (0xffffffff). Hast du noch eine Frage?
Rose Perrone
3
@Mitch: Der größte Wert, der in einer unsigned intDose dargestellt werden kann und von System zu System unterschiedlich ist. Es muss mindestens so sein 65536 , aber es ist üblich 4294967295und kann 18446744073709551615auf einigen Systemen (2 ** 64-1) sein.
Keith Thompson
1
Der größte Wert, den ein 16-Bit-Int ohne Vorzeichen enthalten kann, ist 65535, nicht 65536. Ein kleiner, aber wichtiger Unterschied wie 65536 ist der gleiche wie 0 in einem 16-Bit-Int ohne Vorzeichen.
Sie Raybould
1
@ gnasher729: Bist du dir über den C ++ Standard sicher? Nachdem ich einige Zeit gesucht habe, habe ich den Eindruck, dass sie einfach alle absoluten Garantien für ganzzahlige Bereiche (ausgenommen unsigned char) entfernt haben. Der Standard scheint nirgendwo die Zeichenfolge '65535' oder '65536' zu enthalten, und '+32767' kommt nur in einer Note als mögliche größte Ganzzahl vor, die in dargestellt werden kann (1,9: 9) int. Es wird keine Garantie gegeben, auch wenn INT_MAXdiese nicht kleiner sein kann!
Marc van Leeuwen
51

Der Typ size_t ist der Typ, der vom Operator sizeof zurückgegeben wird. Es ist eine vorzeichenlose Ganzzahl, die die Größe eines auf dem Hostcomputer unterstützten Speicherbereichs in Byte ausdrücken kann. Es ist (normalerweise) mit ptrdiff_t verwandt, dass ptrdiff_t ein vorzeichenbehafteter ganzzahliger Wert ist, so dass sizeof (ptrdiff_t) und sizeof (size_t) gleich sind.

Wenn Sie C-Code schreiben, sollten Sie immer size_t verwenden, wenn Sie sich mit Speicherbereichen befassen.

Der int-Typ hingegen ist im Wesentlichen definiert als die Größe des (vorzeichenbehafteten) Ganzzahlwerts, mit dem der Hostcomputer die Ganzzahlarithmetik am effizientesten ausführen kann. Auf vielen älteren Computern vom Typ PC beträgt der Wert sizeof (size_t) beispielsweise 4 (Byte), sizeof (int) jedoch 2 (Byte). 16-Bit-Arithmetik war schneller als 32-Bit-Arithmetik, obwohl die CPU einen (logischen) Speicherplatz von bis zu 4 GiB verarbeiten konnte.

Verwenden Sie den Typ int nur, wenn Sie Wert auf Effizienz legen, da seine tatsächliche Genauigkeit stark von den Compileroptionen und der Maschinenarchitektur abhängt. Insbesondere spezifiziert der C-Standard die folgenden Invarianten: sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long), wobei der tatsächlichen Darstellung der Genauigkeit, die dem Programmierer für jede von ihnen zur Verfügung steht, keine weiteren Einschränkungen auferlegt werden diese primitiven Typen.

Hinweis: Dies ist NICHT dasselbe wie in Java (das tatsächlich die Bitgenauigkeit für jeden der Typen 'char', 'byte', 'short', 'int' und 'long' angibt).

Kevin S.
quelle
Die De-facto-Definition von int ist, dass es 16 Bit auf 16 Maschinen und 32 Bit auf etwas Größerem ist. Es wurde zu viel Code geschrieben, der davon ausgeht, dass int 32 Bit breit ist, um dies jetzt zu ändern. Daher sollten Benutzer immer size_t oder {, u} int {8,16,32,64} _t verwenden, wenn sie etwas Bestimmtes wünschen - - Vorsichtshalber sollten Benutzer diese nur immer anstelle der ganzzahligen Ganzzahltypen verwenden.
Klarer
3
"Es ist eine vorzeichenlose Ganzzahl, die die Größe eines auf dem Hostcomputer unterstützten Speicherbereichs in Byte ausdrücken kann." -> Nr. size_tKann die Größe eines einzelnen Objekts darstellen (z. B. Anzahl, Array, Struktur). Der gesamte Speicherbereich kannsize_t
chux - Reinstate Monica
"Wenn Sie C-Code schreiben, sollten Sie immer size_t verwenden, wenn Sie sich mit Speicherbereichen befassen." - das bedeutet, dass jeder Index für jedes Array sein sollte size_t- ich hoffe, Sie meinen das nicht so. Meistens beschäftigen wir uns nicht mit Arrays, bei denen die Kardinalität von Adressraum + Portabilität überhaupt eine Rolle spielt. In diesen Fällen würden Sie nehmen size_t. In jedem anderen Fall nehmen Sie Indizes aus (vorzeichenbehafteten) Ganzzahlen. Weil die Verwirrung (die ohne Vorwarnung auftritt), die sich aus dem unerwarteten Unterlaufverhalten von nicht signierten Personen ergibt, häufiger und schlimmer ist als Portabilitätsprobleme, die in den anderen Fällen auftreten können.
johannes_lalala
23

Der Typ size_t muss groß genug sein, um die Größe eines möglichen Objekts zu speichern. Int ohne Vorzeichen muss diese Bedingung nicht erfüllen.

In 64-Bit-Systemen können int und unsigned int beispielsweise 32 Bit breit sein, aber size_t muss groß genug sein, um Zahlen größer als 4G zu speichern

Maciej Hehl
quelle
38
"Objekt" ist die vom Standard verwendete Sprache.
R .. GitHub STOP HELPING ICE
2
Ich denke, es size_tmüsste nur so groß sein, wenn der Compiler einen Typ X akzeptieren könnte, so dass sizeof (X) einen Wert größer als 4G ergeben würde. Die meisten Compiler würden z. B. ablehnen typedef unsigned char foo[1000000000000LL][1000000000000LL]und foo[65536][65536];könnten sogar zu Recht abgelehnt werden, wenn sie eine dokumentierte implementierungsdefinierte Grenze überschreiten.
Supercat
1
@ MattJoiner: Der Wortlaut ist in Ordnung. "Objekt" ist überhaupt nicht vage, sondern definiert als "Speicherbereich".
Leichtigkeitsrennen im Orbit
4

Dieser Auszug aus dem glibc-Handbuch 0.02 kann auch für die Erforschung des Themas relevant sein:

Es gibt ein potenzielles Problem mit dem size_t-Typ und den Versionen von GCC vor Release 2.4. ANSI C erfordert, dass size_t immer ein Typ ohne Vorzeichen ist. Aus stddef.h' to be whatever type the system'sGründen der Kompatibilität mit den Header-Dateien vorhandener Systeme definiert GCC size_t in sys / types.h '. Die meisten Unix-Systeme, die size_t in `sys / types.h 'definieren, definieren es als signierten Typ. Einige Codes in der Bibliothek hängen davon ab, dass size_t ein vorzeichenloser Typ ist, und funktionieren nicht ordnungsgemäß, wenn er signiert ist.

Der GNU C-Bibliothekscode, der erwartet, dass size_t ohne Vorzeichen ist, ist korrekt. Die Definition von size_t als signierter Typ ist falsch. Wir planen, dass GCC in Version 2.4 size_t immer als vorzeichenlosen Typ und fixincludes' script will massage the system'ssys / types.h 'definiert, um keinen Konflikt damit zu verursachen.

In der Zwischenzeit umgehen wir dieses Problem, indem wir GCC explizit anweisen, beim Kompilieren der GNU C-Bibliothek einen vorzeichenlosen Typ für size_t zu verwenden. `configure 'erkennt automatisch, welchen Typ GCC für size_t verwendet, um ihn bei Bedarf zu überschreiben.

Graeme Burke
quelle
2

Wenn mein Compiler auf 32 Bit eingestellt ist, size_tist nichts anderes als ein typedef für unsigned int. Wenn mein Compiler auf 64 Bit eingestellt ist, size_tist nichts anderes als ein typedef für unsigned long long.

Zebrafisch
quelle
1
Kann unter unsigned longeinigen Betriebssystemen wie für beide Fälle definiert werden .
StaceyGirl
-4

size_t ist die Größe eines Zeigers.

In 32 Bit oder dem gemeinsamen ILP32-Modell (Integer, Long, Pointer) beträgt size_t also 32 Bit. und in 64 Bit oder dem üblichen LP64-Modell (Long, Pointer) beträgt size_t 64 Bit (Ganzzahlen sind immer noch 32 Bit).

Es gibt andere Modelle, aber diese werden von g ++ verwendet (zumindest standardmäßig).


quelle
15
size_tist nicht unbedingt so groß wie ein Zeiger, obwohl dies normalerweise der Fall ist. Ein Zeiger muss in der Lage sein, auf eine beliebige Stelle im Speicher zu zeigen. size_tmuss nur groß genug sein, um die Größe des größten Einzelobjekts darzustellen.
Keith Thompson