Qu’est-ce que size_t en C?

Je suis confondu avec size_t en C. Je sais qu’il est renvoyé 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; ?

De Wikipedia :

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

size_t est un type de données non signé défini par plusieurs normes C / C ++, par exemple la norme C99 ISO / IEC 9899, ​​définie dans stddef.h . 1 Il peut être importé davantage en incluant stdlib.h car ce fichier contient en interne stddef.h .

Ce type est utilisé pour représenter la taille d’un object. Les fonctions de bibliothèque qui prennent ou retournent des tailles s’attendent à ce qu’elles soient de type ou aient le type de retour size_t . En outre, l’opérateur sizeof de l’opérateur basé sur le compilateur le plus fréquemment utilisé devrait avoir une valeur constante compatible avec size_t .

En conséquence, size_t est un type garanti pour contenir tout index de tableau.

size_t est un type non signé. Donc, il ne peut représenter aucune valeur négative (<0). Vous l'utilisez lorsque vous comptez quelque chose et vous êtes sûr qu'il 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 object size_t , vous devez vous assurer que, dans tous les contextes utilisés, y compris l’arithmétique, vous voulez des valeurs non négatives. Par exemple, disons que vous avez:

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

et vous voulez trouver la différence des 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, en fonction de votre cas d'utilisation, il peut être préférable d'utiliser int (ou long long ) pour s1 et s2 .

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

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; 

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 glibc sous src/gcc/ginclude/stddef.h dans GCC 4.2.

Apparitions intéressantes de C99

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

    Et comme il est également renvoyé par sizeof , je pense qu’il limite la taille maximale d’un tableau quelconque.

    Voir aussi: La taille maximale d’un tableau dans C

La page de manuel de types.h dit:

size_t doit être un type entier non signé

Comme personne ne l’a encore mentionné, la signification linguistique principale de size_t est que l’opérateur sizeof renvoie une valeur de ce type. De même, la principale signification de ptrdiff_t est que la soustraction d’un pointeur à un autre donnera une valeur de ce type. Les fonctions de bibliothèque qui l’acceptent le font car elles permettent à ces fonctions de fonctionner avec des objects dont la taille dépasse UINT_MAX sur les systèmes où de tels objects peuvent exister, sans forcer les appelants à passer du code supérieur à “unsigned int” suffirait pour tous les objects possibles.

size_t et int ne sont pas interchangeables. Par exemple, sur Linux 64 bits, size_t est de taille 64 bits (ie sizeof(void*) ) mais int est 32 bits.

Notez également que size_t n’est pas signé. Si vous avez besoin d’une version signée, il y a ssize_t sur certaines plates-formes et cela serait plus pertinent pour votre exemple.

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

En général, si vous commencez à 0 et que vous montez, utilisez toujours un type non signé pour éviter un débordement qui vous amènera dans une situation de valeur négative. Ceci est extrêmement important, car si les limites de votre tableau sont inférieures au maximum de votre boucle, mais que votre boucle maximale est supérieure à la valeur maximale de votre type, vous obtiendrez un résultat négatif et vous pourrez 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é.

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.

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

Lorsque nous utilisons un object size_t , nous devons nous assurer que dans tous les contextes utilisés, y compris l’arithmétique, nous ne voulons que 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 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 

D’après ce que je comprends, size_t est un entier unsigned dont la taille est suffisamment grande pour contenir un pointeur de l’architecture native.

Alors:

 sizeof(size_t) >= sizeof(void*)