web-dev-qa-db-fra.com

Qu'est-ce que size_t en C?

Je suis confus avec size_t en C. Je sais qu'il est retourné par l'opérateur sizeof. mais qu'est ce que c'est exactement? Est-ce un type de données?

Disons que j'ai une boucle for:

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

Devrais-je utiliser int i; ou size_t i;?

528
Vijay

De Wikipedia :

Selon la norme ISO C de 1999 (C99), size_t est un entier non signé type d'au moins 16 bits (voir les sections 7.17 et 7.18.3).

size_test un type de données non signé défini par plusieurs normes C/C++, par exemple. la norme C99 ISO/IEC 9899, ​​ qui est défini dans stddef.h. 1 It can être importé par l’inclusion de stdlib.h comme ce fichier en interne sous comprend stddef.h.

Ce type est utilisé pour représenter le taille d'un objet. Fonctions de la bibliothèque que les tailles de reprise ou de retour les attendent être de type ou avoir le type de retour de size_t. En outre, le plus fréquemment utilisé basé sur le compilateur La taille de l'opérateur doit être évaluée à valeur constante compatible avec size_t.

En conséquence, size_t est un type garanti de pouvoir contenir n'importe quel index de tableau.

407
sblom

size_t est un type non signé. Ainsi, il ne peut représenter aucune valeur négative (<0). Vous l'utilisez lorsque vous comptez quelque chose et êtes certain que cela ne peut pas être négatif. Par exemple, strlen() renvoie un size_t car la longueur d'une chaîne doit être au moins égale à 0.

Dans votre exemple, si votre index de boucle doit toujours être supérieur à 0, il peut être judicieux d'utiliser size_t ou tout autre type de données non signé.

Lorsque vous utilisez un objet size_t, vous devez vous assurer que, dans tous les contextes, y compris l'arithmétique, vous souhaitez des valeurs non négatives. Par exemple, supposons que vous ayez:

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

et vous voulez trouver la différence entre les longueurs de str2 et str1. Tu ne peux pas faire:

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

En effet, la valeur affectée à diff sera toujours un nombre positif, même lorsque s2 < s1, car le calcul est effectué avec des types non signés. Dans ce cas, selon votre cas d'utilisation, il vaudrait peut-être mieux utiliser int (ou long long) pour s1 et s2.

Certaines fonctions dans C/POSIX pourraient/devraient utiliser size_t, mais ne le font pas pour des raisons historiques. Par exemple, le deuxième paramètre à fgets devrait idéalement être size_t, mais est int.

195
Alok Singhal

size_t est un type pouvant contenir n'importe quel index de tableau.

Selon l’implémentation, il peut s’agir de:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

Voici comment size_t est défini dans stddef.h de ma machine:

typedef unsigned long size_t;
64
Arjun Sreedharan

Si vous êtes du type empirique,

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

Sortie pour Ubuntu 14.04 64 bits GCC 4.8:

typedef long unsigned int size_t;

Notez que stddef.h est fourni par GCC et non pas glibc sous src/gcc/ginclude/stddef.h dans GCC 4.2.

Apparences intéressantes pour C99

  • malloc prend size_t en argument, il détermine donc la taille maximale pouvant être allouée.

    Et comme il est également retourné par sizeof, je pense que cela limite la taille maximale de tout tableau.

    Voir aussi: Quelle est la taille maximale d'un tableau en C?

La page de manuel de types.h dit:

size_t doit être un type entier non signé

18
codaddict

Comme personne ne l’a encore mentionné, la principale signification linguistique de size_t est que l’opérateur sizeof renvoie une valeur de ce type. De même, la signification principale de ptrdiff_t est que soustraire un pointeur à un autre produira une valeur de ce type. Les fonctions de bibliothèque qui l'acceptent le font car cela leur permettra de fonctionner avec des objets de taille supérieure à UINT_MAX sur des systèmes où de tels objets pourraient exister, sans forcer les appelants à gaspiller du code en transmettant une valeur supérieure à "unsigned int" sur des systèmes suffirait pour tous les objets possibles.

13
supercat

size_t et int ne sont pas interchangeables. Par exemple, sur Linux 64 bits, size_t a une taille de 64 bits (c'est-à-dire sizeof(void*)) mais int est de 32 bits.

Notez également que size_t est non signé. Si vous avez besoin d'une version signée, il existe ssize_t sur certaines plates-formes et ce serait plus pertinent pour votre exemple.

En règle générale, je suggérerais d'utiliser int pour la plupart des cas généraux et de n'utiliser que size_t/ssize_t lorsqu'il en a un besoin spécifique (avec mmap() par exemple).

5
dtoux

Pour expliquer pourquoi size_t devait exister et comment nous en sommes arrivés là:

En termes pragmatiques, size_t et ptrdiff_t ont une largeur de 64 bits sur une implémentation de 64 bits, une largeur de 32 bits sur une implémentation de 32 bits, etc. Ils ne pouvaient forcer aucun type existant à signifier cela, sur chaque compilateur, sans casser le code hérité.

Un size_t ou ptrdiff_t n'est pas nécessairement identique à un intptr_t ou uintptr_t. Elles étaient différentes sur certaines architectures qui étaient encore utilisées lorsque size_t et ptrdiff_t ont été ajoutées à la norme à la fin des années 80 et qui sont devenues obsolètes lorsque C99 a ajouté de nombreux nouveaux types mais n’est pas encore parti (comme Windows 16 bits). Le x86 en mode protégé 16 bits disposait d'une mémoire segmentée dans laquelle le tableau ou la structure la plus grande possible ne pouvait contenir que 65 536 octets, mais un pointeur far devait être large de 32 bits, plus large que les registres. Sur ceux-ci, intptr_t aurait été large de 32 bits, mais size_t et ptrdiff_t pourraient avoir une largeur de 16 bits et entrer dans un registre. Et qui savait quel type de système d'exploitation pourrait être écrit à l'avenir? En théorie, l'architecture i386 propose un modèle de segmentation 32 bits avec des pointeurs 48 bits qu'aucun système d'exploitation n'a encore utilisé.

Le type d'un décalage de mémoire ne peut pas être long car beaucoup trop de code hérité suppose que long a exactement 32 bits de large. Cette hypothèse a même été intégrée dans les API UNIX et Windows. Malheureusement, beaucoup d'autres codes hérités supposaient également qu'une long était suffisamment large pour contenir un pointeur, un décalage de fichier, le nombre de secondes écoulées depuis 1970, etc. POSIX fournit maintenant un moyen standardisé d’imposer la dernière hypothèse à la place de la première, mais ce n’est pas non plus une hypothèse transférable.

Cela ne peut pas être int car seule une infime poignée de compilateurs dans les années 90 a int une largeur de 64 bits. Ensuite, ils sont devenus vraiment bizarres en maintenant long 32 bits de large. La prochaine révision de la norme a déclaré illégal que int soit plus large que long, mais int a toujours une largeur de 32 bits sur la plupart des systèmes 64 bits.

Il ne pouvait pas s'agir de long long int, qui de toute façon a été ajouté ultérieurement, car sa largeur maximale était de 64 bits, même sur les systèmes 32 bits.

Donc, un nouveau type était nécessaire. Même si ce n'était pas le cas, tous ces autres types signifiaient autre chose qu'un décalage dans un tableau ou un objet. Et s'il y avait une leçon à tirer du fiasco de la migration de 32 à 64 bits, il fallait préciser les propriétés qu'un type devait avoir, et ne pas en utiliser une qui signifiait différentes choses dans différents programmes.

4
Davislor

En général, si vous commencez à 0 et montez, utilisez toujours un type non signé pour éviter un débordement, ce qui vous placerait dans une situation de valeur négative. Ceci est extrêmement important, car s'il se trouve que les limites de votre tableau sont inférieures au maximum de votre boucle, mais que votre maximum de boucle se trouve supérieur au maximum de votre type, vous obtiendrez un négatif négatif et vous pourriez rencontrer une erreur de segmentation (SIGSEGV). Donc, en général, n'utilisez jamais int pour une boucle commençant à 0 et allant vers le haut. Utilisez un non signé.

3
Mark

size_t est un type de données entier non signé. Sur les systèmes utilisant la bibliothèque GNU C, ce sera unsigned int ou unsigned long int. size_t est couramment utilisé pour l'indexation de tableaux et le comptage de boucles.

1
Prince

size_t ou tout type non signé peut être vu utilisé comme variable de boucle car les variables de boucle sont généralement supérieures ou égales à 0.

Lorsque nous utilisons un objet size_t , nous devons nous assurer que, dans tous les contextes, y compris l'arithmétique, nous voulons uniquement des valeurs non négatives. Par exemple, le programme suivant donnerait certainement le résultat inattendu:

// 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
bishwas pokharel