it-swarm.com.de

Was ist size_t in C?

Ich verwechsle mich mit size_t in C. Ich weiß, dass es vom sizeof-Operator zurückgegeben wird. Aber was genau ist das? Ist es ein Datentyp?

Nehmen wir an, ich habe eine for-Schleife:

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

Soll ich int i; oder size_t i; verwenden?

528
Vijay

Aus Wikipedia :

Gemäß dem ISO-C-Standard von 1999 (C99), size_t ist eine vorzeichenlose Ganzzahl Typ von mindestens 16 Bit (siehe Abschnitte 7.17 und 7.18.3).

size_tist ein vorzeichenloser Datentyp definiert durch mehrere C/C++ - Standards, z.B. die Norm C99 ISO/IEC 9899, ​​ das ist definiert in stddef.h. 1 Es kann durch Einbeziehung von .__ weiter importiert werden. stdlib.h als diese Datei intern sub enthält stddef.h.

Dieser Typ wird zur Darstellung von .__ verwendet. Größe eines Objekts. Bibliotheksfunktionen die Take- oder Return-Größen erwarten sie vom Typ sein oder den Rückgabetyp haben von size_t. Weiter die meisten häufig Compiler-basierte Operator sizeof sollte eine .__ auswerten. konstanter Wert, der mit .__ kompatibel ist size_t.

Implizit ist size_t ein Typ, der garantiert jeden Array-Index enthält.

407
sblom

size_t ist ein vorzeichenloser Typ. Es kann also keine negativen Werte (<0) darstellen. Sie verwenden es, wenn Sie etwas zählen, und sind sich sicher, dass es nicht negativ sein kann. Zum Beispiel gibt strlen() einen size_t zurück, da die Länge einer Zeichenfolge mindestens 0 sein muss.

Wenn Ihr Schleifenindex in Ihrem Beispiel immer größer als 0 sein wird, kann es sinnvoll sein, size_t oder einen anderen nicht signierten Datentyp zu verwenden.

Wenn Sie ein size_t-Objekt verwenden, müssen Sie sicherstellen, dass in allen Kontexten, einschließlich der Arithmetik, nicht negative Werte gewünscht werden. Angenommen, Sie haben Folgendes:

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

und Sie möchten die Differenz der Längen von str2 und str1 ermitteln. Du kannst nicht tun:

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

Dies liegt daran, dass der Wert, der diff zugewiesen wird, immer eine positive Zahl ist, selbst wenn s2 < s1, da die Berechnung mit unsignierten Typen erfolgt. In diesem Fall können Sie je nach Anwendungsfall int (oder long long) für s1 und s2 verwenden.

Es gibt einige Funktionen in C/POSIX, die size_t verwenden könnten/sollten, aus historischen Gründen jedoch nicht. Der zweite Parameter für fgets sollte idealerweise size_t sein, ist jedoch int.

195
Alok Singhal

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

Je nach Implementierung kann es sich um Folgendes handeln:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

So wird size_t in stddef.h meines Rechners definiert:

typedef unsigned long size_t;
64

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.h von GCC bereitgestellt wird und nicht glibc unter src/gcc/ginclude/stddef.h in GCC 4.2.

Interessante C99-Auftritte

  • malloc nimmt size_t als Argument an, so dass die maximale Größe festgelegt wird, die zugewiesen werden kann.

    Und da es auch von sizeof zurückgegeben wird, schränkt es die maximale Größe eines Arrays ein.

    Siehe auch: Wie groß darf ein Array in C maximal sein?

Die Manpage für types.h sagt:

size_t ist ein vorzeichenloser Integer-Typ

18
codaddict

Da es bisher noch nicht erwähnt wurde, ist die sprachliche Bedeutung von size_t vor allem, dass der Operator sizeof einen Wert dieses Typs zurückgibt. Ebenso ist die wichtigste Bedeutung von ptrdiff_t, dass das Subtrahieren eines Zeigers von einem anderen einen Wert dieses Typs ergibt. Bibliotheksfunktionen, die dies akzeptieren, tun dies, weil sie es ermöglichen, dass diese Funktionen mit Objekten arbeiten, deren Größe UINT_MAX auf Systemen überschreitet, auf denen solche Objekte vorhanden sein könnten, ohne dass Aufrufer Code zwingen müssen, Code zu verschwenden, der auf Systemen mit dem größeren Typ einen Wert überschreitet, der größer als "unsigned int" ist würde für alle möglichen Objekte ausreichen.

13
supercat

size_t und int sind nicht austauschbar. Unter 64-Bit-Linux ist size_t beispielsweise 64-Bit groß (d. H. sizeof(void*)), aber int ist 32-Bit.

Beachten Sie auch, dass size_t nicht signiert ist. Wenn Sie eine signierte Version benötigen, gibt es auf einigen Plattformen ssize_t und dies wäre für Ihr Beispiel relevanter.

Generell würde ich empfehlen, int für die meisten allgemeinen Fälle zu verwenden und size_t/ssize_t nur dann zu verwenden, wenn ein bestimmter Bedarf besteht (beispielsweise mit mmap()).

5
dtoux

Um herauszufinden, warum size_t existieren musste und wie wir hierher gekommen sind:

In pragmatischen Ausdrücken sind size_t und ptrdiff_t bei einer 64-Bit-Implementierung garantiert 64 Bit breit, bei einer 32-Bit-Implementierung 32 Bit breit und so weiter. Sie konnten keinen vorhandenen Typ dazu zwingen, dies auf jedem Compiler zu bedeuten, ohne den alten Code zu brechen.

Ein size_t oder ptrdiff_t ist nicht unbedingt dasselbe wie ein intptr_t oder uintptr_t. Sie unterschieden sich in bestimmten Architekturen, die noch verwendet wurden, als size_t und ptrdiff_t in den späten 80er Jahren zum Standard hinzugefügt wurden, und wurden obsolet, als C99 viele neue Typen hinzufügte, aber noch nicht weg war (wie 16-Bit-Windows). Der x86-Speicher im geschützten 16-Bit-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, aber ein Zeiger für far musste 32 Bit breit und breiter als die Register sein. Auf diesen wäre intptr_t 32 Bit breit gewesen, aber size_t und ptrdiff_t könnten 16 Bit breit sein und in ein Register passen. Und wer wusste, welches Betriebssystem in Zukunft geschrieben werden könnte? Theoretisch bietet die i386-Architektur ein 32-Bit-Segmentierungsmodell mit 48-Bit-Zeigern, das noch nie von einem Betriebssystem verwendet wurde.

Der Typ eines Speicher-Offsets kann nicht long sein, da bei viel zu viel Legacy-Code davon ausgegangen wird, dass long genau 32 Bit breit ist. Diese Annahme wurde sogar in die UNIX- und Windows-APIs integriert. Leider haben auch viele andere ältere Codes davon ausgegangen, dass eine long breit genug ist, um einen Zeiger, einen Datei-Offset, die Anzahl der seit 1970 verstrichenen Sekunden usw. aufzunehmen. POSIX bietet jetzt eine standardisierte Möglichkeit, um die letztere Annahme als wahr zu erzwingen, aber auch keine tragbare Annahme.

Es könnte nicht int sein, da nur eine kleine Handvoll Compiler in den 90er Jahren int mit einer Breite von 64 Bit erstellt hat. Dann wurden sie wirklich komisch, indem sie long 32 Bit breit hielten. Die nächste Überarbeitung des Standards erklärte es für illegal, dass int breiter als long ist, aber int ist auf den meisten 64-Bit-Systemen immer noch 32 Bit breit.

Es konnte sich nicht um long long int handeln, der ohnehin später hinzugefügt wurde, da er selbst auf 32-Bit-Systemen mit einer Breite von mindestens 64 Bit erstellt wurde.

Also wurde 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 Lektion aus dem Fiasko der 32-zu-64-Bit-Migration gab, dann war es genau zu sagen, welche Eigenschaften ein Typ haben musste, und keine, die verschiedene Dinge in verschiedenen Programmen bedeutete.

4
Davislor

Wenn Sie bei 0 beginnen und aufwärts gehen, verwenden Sie im Allgemeinen immer einen vorzeichenlosen Typ, um einen Überlauf zu vermeiden, der Sie in eine Situation mit negativem Wert führt. Dies ist von entscheidender Bedeutung, denn wenn Ihre Array-Grenzen unter dem Maximalwert Ihrer Schleife liegen, der Maximalwert jedoch größer als der Maximalwert Ihres Typs ist, werden Sie negativ umlaufen und es kommt zu einem Segmentierungsfehler (SIGSEGV). Verwenden Sie also im Allgemeinen niemals int für eine Schleife, die bei 0 beginnt und aufwärts geht. Verwenden Sie einen vorzeichenlosen.

3
Mark

size_t ist ein vorzeichenloser Integer-Datentyp. Auf Systemen, die die C-Bibliothek GNU verwenden, ist dies vorzeichenlos int oder unsigned long int. size_t wird üblicherweise für die Array-Indexierung und die Schleifenzählung verwendet.

1
Prince

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

Wenn wir ein size_t object 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
0