Was ist size_t in C?


626

Ich bin verwirrt mit size_tin C. Ich weiß, dass es vom sizeofBediener zurückgegeben wird. Aber was genau ist das? Ist es ein Datentyp?

Angenommen, ich habe eine forSchleife:

for(i = 0; i < some_size; i++)

Soll ich verwenden int i;oder size_t i;?


11
Wenn dies Ihre einzigen Optionen sind, verwenden Sie intif some_sizeist signiert, size_twenn es nicht signiert ist.
Nate

8
@Nate Das ist falsch. POSIX hat den Typ ssize_t, aber der tatsächlich richtige Typ ist ptrdiff_t.
Steven Stewart-Gallus

2
Die Antworten sind nicht so klar wie bei der Low-Level-Programmierung: C, Assembly und Programmausführung unter Intel® 64 . Wie im Buch angegeben, reicht die Verwendung eines Index int imöglicherweise nicht aus, um ein großes Array zu adressieren. size_t iWenn Sie also verwenden , können Sie mehr Indizes adressieren. Selbst wenn Sie ein riesiges Array haben, sollte dies kein Problem sein. size_tist ein Datentyp: normalerweise a unsigned long int, dies hängt jedoch von Ihrem System ab.
Bruno

Antworten:


461

Aus Wikipedia :

Gemäß der ISO C-Norm von 1999 (C99) size_thandelt es sich um einen vorzeichenlosen Integer-Typ von mindestens 16 Bit (siehe Abschnitte 7.17 und 7.18.3).

size_tist ein vorzeichenloser Datentyp, der durch mehrere C / C ++ - Standards definiert ist, z. B. den C99 ISO / IEC 9899-Standard, der in definiert ist stddef.h. 1 Es kann weiter importiert werden, indem stdlib.hdiese Datei intern untergeordnet wird stddef.h.

Dieser Typ wird verwendet, um die Größe eines Objekts darzustellen. Bibliotheksfunktionen, die Größen annehmen oder zurückgeben, erwarten, dass sie vom Typ sind oder den Rückgabetyp haben size_t. Darüber hinaus sollte die am häufigsten verwendete compilerbasierte Operatorgröße von einen konstanten Wert ergeben, der mit kompatibel ist size_t.

Folglich size_tist ein Typ garantiert, der jeden Array-Index enthält.


4
"Bibliotheksfunktionen, die Größen annehmen oder zurückgeben, erwarten, dass sie vom Typ ... size_t sind" Außer dass stat () off_t für die Größe einer Datei verwendet
Draemon

64
@Draemon Dieser Kommentar spiegelt eine grundlegende Verwirrung wider. size_tist für Objekte im Speicher. Der C-Standard definiert nicht einmal stat()oder off_t(das sind POSIX-Definitionen) oder irgendetwas, das mit Festplatten oder Dateisystemen zu tun hat - er stoppt sich bei FILEStreams. Die Verwaltung des virtuellen Speichers unterscheidet sich in Bezug auf die Größenanforderungen grundlegend von Dateisystemen und der Dateiverwaltung. Daher off_tist die Erwähnung hier irrelevant.
jw013

3
@ jw013: Ich würde es kaum als grundlegende Verwirrung bezeichnen, aber Sie machen einen interessanten Punkt. Der zitierte Text sagt jedoch nicht "Größen von In-Memory-Objekten" aus, und "Offset" ist kaum ein guter Name für einen Größentyp, unabhängig davon, wo er gerade gespeichert ist.
Draemon

30
@ Draemon Guter Punkt. Diese Antwort zitiert Wikipedia, das meiner Meinung nach in diesem Fall nicht die beste Erklärung hat. Der C-Standard selbst ist viel klarer: Er definiert size_tals den Typ des Ergebnisses des sizeofOperators (ungefähr 7.17p2 <stddef.h>). In Abschnitt 6.5 wird genau erläutert, wie C-Ausdrücke funktionieren (6.5.3.4 für sizeof). Da Sie keine sizeofDatenträgerdatei anwenden können (hauptsächlich, weil C nicht einmal definiert, wie Datenträger und Dateien funktionieren), besteht kein Raum für Verwirrung. Mit anderen Worten, beschuldigen Sie Wikipedia (und diese Antwort für das Zitieren von Wikipedia und nicht des tatsächlichen C-Standards).
jw013

2
@Draemon - Ich würde auch der Einschätzung der "grundlegenden Verwirrung" zustimmen. Wenn Sie die C / C ++ - Standards nicht gelesen haben, denken Sie möglicherweise, dass "Objekt" sich auf "objektorientierte Programmierung" bezieht, was nicht der Fall ist. Lesen Sie den C-Standard, der keines dieser OOP-Objekte enthält, aber dennoch Objekte enthält, und finden Sie es heraus. Die Antwort kann Sie überraschen!
Heath Hunnicutt

220

size_tist ein vorzeichenloser Typ. Es können also keine negativen Werte (<0) dargestellt werden. Sie verwenden es, wenn Sie etwas zählen, und sind sicher, dass es nicht negativ sein kann. Gibt beispielsweise a strlen()zurück, size_tda die Länge eines Strings mindestens 0 sein muss.

Wenn in Ihrem Beispiel Ihr Schleifenindex immer größer als 0 sein soll, ist die Verwendung size_teines anderen vorzeichenlosen Datentyps möglicherweise sinnvoll .

Wenn Sie ein size_tObjekt verwenden, müssen Sie sicherstellen, dass Sie in allen verwendeten Kontexten, einschließlich der Arithmetik, nicht negative Werte wünschen. Nehmen wir zum Beispiel an, Sie haben:

size_t s1 = strlen(str1);
size_t s2 = strlen(str2);

und Sie möchten den Unterschied der Längen von str2und finden str1. Du kannst nicht tun:

int diff = s2 - s1; /* bad */

Dies liegt daran, dass der zugewiesene Wert diffimmer eine positive Zahl ist, auch wenn s2 < s1die Berechnung mit vorzeichenlosen Typen durchgeführt wird. In diesem Fall ist es je nach Anwendungsfall möglicherweise besser, int(oder long long) für s1und zu verwenden s2.

Es gibt einige Funktionen in C / POSIX, die verwendet werden könnten / sollten size_t, aber aus historischen Gründen nicht. Zum Beispiel sollte der zweite Parameter fgetsidealerweise sein size_t, ist es aber int.


8
@Alok: Zwei Fragen: 1) Wie groß ist das size_t? 2) Warum sollte ich so size_tetwas vorziehen unsigned int?
Lazer

2
@Lazer: die Größe von size_tist sizeof(size_t). Der C-Standard garantiert, dass SIZE_MAXmindestens 65535 vorliegt. Dies size_tist der vom sizeofOperator zurückgegebene Typ und wird in der Standardbibliothek verwendet (z. B. strlenRückgabe size_t). Wie Brendan sagte, size_tmuss nicht dasselbe sein wie unsigned int.
Alok Singhal

4
@Lazer - ja, es size_twird garantiert ein nicht signierter Typ.
Alok Singhal

2
@Celeritas nein, ich meine, dass ein vorzeichenloser Typ nur nicht negative Werte darstellen kann. Ich hätte wahrscheinlich sagen sollen "Es kann keine negativen Werte darstellen".
Alok Singhal

4
@ JasonOster, Zweierkomplement ist im C-Standard keine Anforderung. Wenn der Wert von s2 - s1überläuft int, ist das Verhalten undefiniert.
Alok Singhal

73

size_t ist ein Typ, der jeden Array-Index enthalten kann.

Abhängig von der Implementierung kann es sich um Folgendes handeln:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

So size_tist in stddef.hmeiner Maschine definiert:

typedef unsigned long size_t;

4
Sicherlich typedef unsigned long size_tist der Compiler abhängig. Oder schlagen Sie vor, dass es immer so ist?
chux

4
@chux: Nur weil eine Implementierung sie als solche definiert, heißt das noch lange nicht, dass alle dies tun. Ein typisches Beispiel: 64-Bit-Windows. unsigned longist 32-Bit, size_tist 64-Bit.
Tim

2
Was genau ist der Zweck von size_t? Wenn ich eine Variable für mich selbst erstellen kann wie: "int mysize_t;" oder "long mysize_t" oder "unsigned long mysize_t". Warum sollte jemand diese Variable für mich erstellt haben?
Midkin

1
@midkin size_tist keine Variable. Dies ist ein Typ, den Sie verwenden können, wenn Sie die Größe eines Objekts im Speicher darstellen möchten.
Arjun Sreedharan

1
size_tStimmt es, dass auf einem 32-Bit-Computer immer 32 Bit und 64 Bit ebenfalls vorhanden sind?
John Wu

70

Wenn Sie der empirische Typ sind ,

echo | gcc -E -xc -include 'stddef.h' - | grep size_t

Ausgabe für Ubuntu 14.04 64-Bit GCC 4.8:

typedef long unsigned int size_t;

Beachten Sie, dass stddef.hdies von GCC bereitgestellt wird und nicht unter src/gcc/ginclude/stddef.hGCC 4.2 angezeigt wird.

Interessante C99 Auftritte

  • malloc nimmt size_t als Argument, so bestimmt es die maximale Größe, die zugewiesen werden kann.

    Und da wird es auch von zurückgegeben sizeof , begrenzt es meiner Meinung nach die maximale Größe eines Arrays.

    Siehe auch: Was ist die maximale Größe eines Arrays in C?


1
Ich habe die gleiche Umgebung, habe sie jedoch auf 32 Bit getestet und die GCC-Option "-m32" übergeben. Das Ergebnis war: "typedef unsigned int size_t". Vielen Dank, dass Sie diesen großartigen Befehl @Ciro geteilt haben. Er hat mir sehr geholfen! :-)
Silvioprog

2
Die Sache selbst ist nicht verwirrend. Es ist der verwirrende Verstand, der versucht, viele Fragen zu stellen und viele Antworten zu geben. Ich bin überrascht, dass diese Antwort und die von Arjun Sreedharan die Menschen immer noch nicht davon abhalten, zu fragen und zu antworten.
Biocyberman

1
Tolle Antwort, denn es sagt Ihnen tatsächlich, was es size_tist , zumindest in einer beliebten Linux-Distribution.
Andrey Portnoy


19

Da es noch niemand erwähnt hat, besteht die primäre sprachliche Bedeutung size_tdarin, dass der sizeofOperator einen Wert dieses Typs zurückgibt. Ebenso ist die Hauptbedeutung von, ptrdiff_tdass das Subtrahieren eines Zeigers von einem anderen einen Wert dieses Typs ergibt. Bibliotheksfunktionen, die dies akzeptieren, tun dies, weil solche Funktionen auf Objekten, auf denen solche Objekte vorhanden sein könnten, mit Objekten arbeiten können, deren Größe UINT_MAX überschreitet, ohne dass Aufrufer gezwungen werden, Code zu verschwenden, der auf Systemen mit dem größeren Typ einen Wert über "unsigned int" übergibt würde für alle möglichen Objekte ausreichen.


Meine Frage war schon immer: Wenn sizeof nie existiert hätte, würde es einen Bedarf für size_t geben?
Dean P

@ DeanP: Vielleicht nicht, obwohl es dann eine Frage geben würde, welcher Argumenttyp für Dinge wie verwendet werden sollte malloc(). Persönlich würde ich gern noch gesehen Versionen habe , die Argumente vom Typ nehmen int, longund long long, mit einigen Implementierungen fördern kürzeren Typen und andere Implementierung zB lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);}[auf einigen Plattformen zu nennen imalloc(123)wäre billiger als ein Anruf lmalloc(123);, und sogar auf einer Plattform , wo size_t16 Bits, Code, der die Größe zuweisen möchte, die in einem "langen" Wert berechnet wurde ...
Supercat

... sollte sich darauf verlassen können, dass die Zuordnung fehlschlägt, wenn der Wert größer ist, als der Zuweiser verarbeiten kann.
Superkatze

11

Um zu erklären, warum size_tes existieren musste und wie wir hierher kamen:

In pragmatischen Begriffen size_tund ptrdiff_tbei einer 64-Bit-Implementierung mit einer Breite von 64 Bit, bei einer 32-Bit-Implementierung mit einer Breite von 32 Bit usw. garantiert. Sie konnten keinen vorhandenen Typ dazu zwingen, dies auf jedem Compiler zu bedeuten, ohne den alten Code zu beschädigen.

Ein size_toder ptrdiff_tist nicht unbedingt dasselbe wie ein intptr_toder uintptr_t. Sie waren anders auf bestimmte Architekturen , die noch in Gebrauch waren , als size_tund ptrdiff_twurden dem Standard - in den späten 80er Jahren hinzugefügt und veralten , wenn C99 viele neue Arten hinzugefügt , aber noch nicht gegangen (wie 16-Bit - Windows). Das x86 im 16-Bit-geschützten Modus hatte einen segmentierten Speicher, in dem das größtmögliche Array oder die größtmögliche Struktur nur 65.536 Byte groß sein konnte, ein farZeiger jedoch 32 Bit breit und breiter als die Register sein musste. Auf denen intptr_twäre aber 32 Bit breit gewesen size_tundptrdiff_tkönnte 16 Bit breit sein und in ein Register passen. Und wer wusste, welche Art von Betriebssystem in Zukunft geschrieben werden könnte? Theoretisch bietet die i386-Architektur ein 32-Bit-Segmentierungsmodell mit 48-Bit-Zeigern, das noch kein Betriebssystem verwendet hat.

Der Typ eines Speicherversatzes könnte nicht sein, longweil viel zu viel Legacy-Code davon ausgeht, dass er longgenau 32 Bit breit ist. Diese Annahme wurde sogar in die UNIX- und Windows-APIs integriert. Leider gingen viele andere Legacy-Codes auch davon aus, dass a longbreit genug ist, um einen Zeiger, einen Datei-Offset, die Anzahl der Sekunden, die seit 1970 vergangen sind, usw. aufzunehmen. POSIX bietet jetzt eine standardisierte Möglichkeit, die letztgenannte Annahme anstelle der ersteren als wahr zu erzwingen, aber es ist auch keine tragbare Annahme zu treffen.

Es konnte nicht sein, intdass nur eine winzige Handvoll Compiler in den 90er Jahren int64 Bit breit waren. Dann wurden sie wirklich komisch, indem sie long32 Bit breit hielten . Die nächste Überarbeitung des Standards erklärte es für illegal int, breiter als zu sein long, ist aber intauf den meisten 64-Bit-Systemen immer noch 32 Bit breit.

Es konnte nicht sein long long int, was ohnehin später hinzugefügt wurde, da dieses selbst auf 32-Bit-Systemen mindestens 64 Bit breit war.

Es wurde also ein neuer Typ benötigt. Selbst wenn dies nicht der Fall wäre, bedeuteten all diese anderen Typen etwas anderes als einen Versatz innerhalb eines Arrays oder Objekts. Und wenn es eine Lehre aus dem Fiasko der 32-zu-64-Bit-Migration gab, sollte genau angegeben werden, welche Eigenschaften ein Typ haben muss, und nicht eine, die in verschiedenen Programmen unterschiedliche Bedeutungen hat.


Nicht einverstanden mit " size_tund ptrdiff_tbei einer 64-Bit-Implementierung sind sie garantiert 64 Bit breit" usw. Die Garantie ist überbewertet. Der Bereich von size_twird hauptsächlich von der Speicherkapazität der Implementierung bestimmt. "eine n-Bit-Implementierung" ist hauptsächlich die native Prozessorbreite von ganzen Zahlen. Sicherlich verwenden viele Implementierungen eine Speichergröße und Prozessorbusbreite ähnlicher Größe, aber es gibt breite native Ganzzahlen mit wenig Speicher oder schmale Prozessoren mit viel Speicher, die diese beiden Implementierungseigenschaften auseinander treiben.
chux - Monica

8

size_tund intsind nicht austauschbar. Zum Beispiel size_tist Linux unter 64-Bit 64-Bit groß (dh sizeof(void*)), aber int32-Bit.

Beachten Sie auch, dass dies size_tnicht signiert ist. Wenn Sie eine signierte Version benötigen, gibt es diese ssize_tauf einigen Plattformen und sie wäre für Ihr Beispiel relevanter.

In der Regel würde ich mit vorschlagen intfür die meisten allgemeinen Fälle und nur verwenden size_t/ ssize_twenn es ein besonderer Bedarf dafür ist (mit mmap()zum Beispiel).


3

Wenn Sie bei 0 beginnen und nach oben gehen, verwenden Sie im Allgemeinen immer einen vorzeichenlosen Typ, um einen Überlauf zu vermeiden, der Sie in eine negative Wertsituation führt. Dies ist von entscheidender Bedeutung, denn wenn Ihre Array-Grenzen zufällig kleiner als das Maximum Ihrer Schleife sind, Ihr Schleifenmaximum jedoch größer als das Maximum Ihres Typs ist, werden Sie negativ umbrochen und es kann zu einem Segmentierungsfehler (SIGSEGV) kommen ). Verwenden Sie daher im Allgemeinen niemals int für eine Schleife, die bei 0 beginnt und nach oben geht. Verwenden Sie eine nicht signierte.


3
Ich kann Ihre Argumentation nicht akzeptieren. Sie sagen, es ist besser, dass der Überlauffehler stillschweigend zum Zugriff auf gültige Daten in Ihrem Array führt?
Maf-Soft

1
@ Maf-Soft ist richtig. Wenn der Fehler unentdeckt bleibt, ist er schlimmer als ein Programmabsturz. Warum hat diese Antwort positive Stimmen bekommen?
yoyo_fun

Wenn auf gültige Daten in Ihrem Array zugegriffen wird, handelt es sich nicht um einen Fehler, da der nicht signierte Typ bei dem maximal signierten Typ nicht überläuft. Was ist das für eine Logik? Nehmen wir an, Sie verwenden aus irgendeinem Grund char, um über ein Array mit 256 Elementen zu iterieren. Das signierte Element läuft bei 127 über und das 128. Element wird sigsegv. Wenn Sie jedoch unsigned verwenden, wird das gesamte Array wie beabsichtigt durchlaufen. Andererseits, wenn Sie ein int verwenden, werden Ihre Arrays nicht wirklich größer als 2 Milliarden Elemente sein, so oder so spielt es keine Rolle ...
Purple Ice

1
Ich kann mir keine Situation vorstellen, in der ein ganzzahliger Überlauf kein Fehler ist, egal ob er positiv oder negativ ist. Nur weil Sie keinen Segfault bekommen, heißt das nicht, dass Sie das richtige Verhalten sehen! Und Sie können einen Segmentierungsfehler feststellen oder nicht, unabhängig davon, ob Ihr Offset positiv oder negativ ist. Alles hängt von Ihrem Speicherlayout ab. @PurpleIce, ich glaube nicht, dass Sie dasselbe sagen wie diese Antwort; Ihr Argument scheint zu sein, dass Sie einen Datentyp auswählen sollten, der groß genug ist, um den größten Wert aufzunehmen, den Sie in ihn einfügen möchten. Dies ist einfach nur gesunder Menschenverstand.
Soren Bjornstad

Trotzdem bevorzuge ich die semantische Verwendung eines vorzeichenlosen Typs für Schleifenindizes . Wenn Ihre Variable niemals negativ sein wird, können Sie dies auch in dem von Ihnen gewählten Typ angeben. Es könnte dem Compiler auch ermöglichen, einen Fehler zu erkennen, bei dem der Wert negativ ausfiel, obwohl GCC zumindest ziemlich schrecklich darin ist, diesen bestimmten Fehler zu erkennen (einmal habe ich ein vorzeichenloses mit -1 initialisiert und keine Warnung erhalten). In ähnlicher Weise ist ein size_t semantisch für Array-Indizes geeignet.
Soren Bjornstad

3

size_t ist ein vorzeichenloser ganzzahliger Datentyp. Auf Systemen, die die GNU C-Bibliothek verwenden, ist dies int ohne Vorzeichen oder long int ohne Vorzeichen. size_t wird üblicherweise für die Array-Indizierung und Schleifenzählung verwendet.


1

size_t oder ein beliebiger vorzeichenloser Typ kann als Schleifenvariable verwendet werden, da Schleifenvariablen normalerweise größer oder gleich 0 sind.

Wenn wir ein size_t- Objekt verwenden, müssen wir sicherstellen, dass in allen verwendeten Kontexten, einschließlich der Arithmetik, nur nicht negative Werte gewünscht werden. Zum Beispiel würde folgendes Programm definitiv das unerwartete Ergebnis liefern:

// C program to demonstrate that size_t or
// any unsigned int type should be used 
// carefully when used in a loop

#include<stdio.h>
int main()
{
const size_t N = 10;
int a[N];

// This is fine
for (size_t n = 0; n < N; ++n)
a[n] = n;

// But reverse cycles are tricky for unsigned 
// types as can lead to infinite loop
for (size_t n = N-1; n >= 0; --n)
printf("%d ", a[n]);
}

Output
Infinite loop and then segmentation fault

1

size_tist ein vorzeichenloser Integer-Datentyp, der nur 0 und mehr als 0 Integer-Werte zuweisen kann. Es misst Bytes der Größe eines Objekts und wird vom sizeofOperator zurückgegeben. constist die Syntaxdarstellung von size_t, aber ohne dass constSie das Programm ausführen können.

const size_t number;

size_tWird regelmäßig für die Array-Indizierung und Schleifenzählung verwendet. Wenn der Compiler ist 32-bit, würde es funktionieren unsigned int. Wenn der Compiler ist 64-bit, würde es auch funktionieren unsigned long long int. Dort für maximale Größe size_tje nach Compilertyp.

size_tdefiniert bereits auf <stdio.h>Header - Datei, aber es kann auch durch definieren <stddef.h>, <stdlib.h>, <string.h>, <time.h>, <wchar.h>Header.

  • Beispiel (mit const)
#include <stdio.h>

int main()
{
    const size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Ausgabe -: size = 800


  • Beispiel (ohne const)
#include <stdio.h>

int main()
{
    size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Ausgabe -: size = 800


-3

Nach meinem Verständnis size_thandelt es sich um eine unsignedGanzzahl, deren Bitgröße groß genug ist, um einen Zeiger auf die native Architektur aufzunehmen.

Damit:

sizeof(size_t) >= sizeof(void*)

16
Nicht wahr. Die Zeigergröße kann größer sein als die size_t. Einige Beispiele: C-Compiler im x86-Real-Modus können 32 Bit FARoder HUGEZeiger haben, aber size_t beträgt immer noch 16 Bit. Ein weiteres Beispiel: Watcom C hatte früher einen speziellen Fettzeiger für erweiterten Speicher, der 48 Bit breit war, aber size_tnicht. Auf einem eingebetteten Controller mit Harvard-Architektur haben Sie auch keine Korrelation, da beide unterschiedliche Adressräume betreffen.
Patrick Schlüter

1
Und auf diesem stackoverflow.com/questions/1572099/… gibt es weitere Beispiele AS / 400 mit 128-Bit-Zeigern und 32-Bitsize_t
Patrick Schlüter

Das ist offensichtlich falsch. Lassen Sie es uns jedoch hier behalten
Antti Haapala
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.