unsigned int vs size_t

492

Je remarque que le code C et C ++ moderne semble utiliser à la size_tplace de int/ à unsigned intpeu près partout - des paramètres des fonctions de chaîne C à la STL. Je suis curieux de connaître la raison de cela et les avantages qu'il apporte.

Rob
la source

Réponses:

388

Le size_ttype est le type entier non signé qui est le résultat de l' sizeofopérateur (et de l' offsetofopérateur), il est donc garanti d'être suffisamment grand pour contenir la taille du plus grand objet que votre système peut gérer (par exemple, un tableau statique de 8 Go).

Le size_ttype peut être supérieur, égal ou inférieur à an unsigned int, et votre compilateur peut faire des hypothèses à ce sujet pour l'optimisation.

Vous pouvez trouver des informations plus précises dans la norme C99, section 7.17, dont un projet est disponible sur Internet au format pdf , ou dans la norme C11, section 7.19, également disponible en version pdf .

Remo.D
la source
50
Nan. Pensez à x86-16 avec le grand modèle de mémoire (pas énorme): les pointeurs sont loin (32 bits), mais les objets individuels sont limités à 64k (donc size_t peut être 16 bits).
dan04
8
"la taille du plus grand objet" n'est pas une mauvaise formulation, mais tout à fait correcte. Le sixe d'un objet peut être beaucoup plus limité que l'espace d'adressage.
gnasher729
3
"votre compilateur pourrait faire des suppositions à ce sujet": j'espère que le compilateur connaît la plage exacte de valeurs qui size_tpeut représenter! Si ce n'est pas le cas, qui le fait?
Marc van Leeuwen
4
@Marc: Je pense que le point était plus que le compilateur pourrait être capable de faire quelque chose avec cette connaissance.
8
Je souhaite juste que ce type de plus en plus populaire ne nécessite pas l'inclusion d'un fichier d'en-tête.
user2023370
98

Le C classique (le premier dialecte du C décrit par Brian Kernighan et Dennis Ritchie dans The C Programming Language, Prentice-Hall, 1978) n'a pas fourni size_t. Introduction du comité des normes C size_tpour éliminer un problème de portabilité

Expliqué en détail sur embedded.com (avec un très bon exemple)

azeemarif
la source
6
Un autre excellent article expliquant à la fois size_t et ptrdiff_t: viva64.com/en/a/0050
Ihor Kaharlichenko
74

En bref, il size_tn'est jamais négatif et optimise les performances car il est typé comme étant le type entier non signé qui est assez grand - mais pas trop grand - pour représenter la taille du plus grand objet possible sur la plate-forme cible.

Les tailles ne doivent jamais être négatives et sont en effet size_tun type non signé. De plus, parce qu'il size_tn'est pas signé, vous pouvez stocker des nombres qui sont environ deux fois plus gros que dans le type signé correspondant, car nous pouvons utiliser le bit de signe pour représenter la magnitude, comme tous les autres bits de l'entier non signé. Lorsque nous gagnons un bit de plus, nous multiplions la plage de nombres que nous pouvons représenter par un facteur d'environ deux.

Alors, demandez-vous, pourquoi ne pas simplement utiliser un unsigned int? Il peut ne pas être en mesure de contenir des nombres suffisamment importants. Dans une implémentation où unsigned intest 32 bits, le plus grand nombre qu'il peut représenter est 4294967295. Certains processeurs, tels que l'IP16L32, peuvent copier des objets plus grands que les 4294967295octets.

Alors, demandez-vous, pourquoi ne pas utiliser un unsigned long int? Il impose un péage de performance sur certaines plates-formes. La norme C exige qu’une longoccupe au moins 32 bits. Une plate-forme IP16L32 implémente chaque 32 bits de long comme une paire de mots de 16 bits. Presque tous les opérateurs 32 bits sur ces plates-formes nécessitent deux instructions, sinon plus, car ils fonctionnent avec les 32 bits dans deux blocs 16 bits. Par exemple, le déplacement d'une longueur de 32 bits nécessite généralement deux instructions machine - une pour déplacer chaque bloc de 16 bits.

L'utilisation size_tévite ce péage de performance. Selon cet article fantastique , "Type size_test un typedef qui est un alias pour un type d'entier non signé, généralement unsigned intou unsigned long, mais peut-être même unsigned long long. Chaque implémentation Standard C est censée choisir l'entier non signé qui est assez grand - mais pas plus grand que nécessaire - pour représenter la taille du plus grand objet possible sur la plate-forme cible. "

Rose Perrone
la source
1
Désolé de commenter cela après si longtemps, mais je viens de confirmer le plus grand nombre qu'un int non signé peut contenir - peut-être que je comprends mal votre terminologie, mais je pensais que le plus grand nombre qu'un int non signé peut contenir est 4294967295, 65356 étant le maximum d'un court non signé.
Mitch
Si votre entier non signé occupe 32 bits, alors oui, le plus grand nombre qu'il peut contenir est 2 ^ 32 - 1, qui est 4294967295 (0xffffffff). Avez-vous une autre question?
Rose Perrone
3
@Mitch: La plus grande valeur qui peut être représentée dans une unsigned intboîte et varie d'un système à l'autre. Il doit être au moins 65536 , mais il est courant 4294967295et pourrait être 18446744073709551615(2 ** 64-1) sur certains systèmes.
Keith Thompson
1
La valeur la plus élevée qu'un int 16 bits non signé peut contenir est 65535, pas 65536. Une petite mais importante différence comme 65536 est la même que 0 dans un int 16 bits non signé.
Sie Raybould
1
@ gnasher729: Êtes-vous sûr de la norme C ++? Après avoir cherché pendant un certain temps, j'ai l'impression qu'ils ont simplement supprimé toutes les garanties absolues sur les plages entières (à l'exclusion unsigned char). La norme ne semble pas contenir la chaîne '65535' ou '65536' n'importe où, et '+32767' ne se produit que (1.9: 9) dans une note comme le plus grand entier possible représentable dans int; aucune garantie n'est donnée même qui INT_MAXne peut être inférieure à cela!
Marc van Leeuwen
51

Le type size_t est le type renvoyé par l'opérateur sizeof. Il s'agit d'un entier non signé capable d'exprimer la taille en octets de n'importe quelle plage de mémoire prise en charge sur la machine hôte. Il est (généralement) lié à ptrdiff_t en ce que ptrdiff_t est une valeur entière signée telle que sizeof (ptrdiff_t) et sizeof (size_t) sont égaux.

Lors de l'écriture de code C, vous devez toujours utiliser size_t lorsque vous traitez avec des plages de mémoire.

Le type int, d'autre part, est essentiellement défini comme la taille de la valeur entière (signée) que la machine hôte peut utiliser pour effectuer le plus efficacement l'arithmétique entière. Par exemple, sur de nombreux ordinateurs de type PC plus anciens, la valeur sizeof (size_t) serait de 4 (octets) mais sizeof (int) serait de 2 (octets). L'arithmétique 16 bits était plus rapide que l'arithmétique 32 bits, bien que le CPU puisse gérer un espace mémoire (logique) allant jusqu'à 4 Gio.

N'utilisez le type int que lorsque vous vous souciez de l'efficacité car sa précision réelle dépend fortement des options du compilateur et de l'architecture de la machine. En particulier, la norme C spécifie les invariants suivants: sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long) ne plaçant aucune autre limitation sur la représentation réelle de la précision disponible pour le programmeur pour chacun des ces types primitifs.

Remarque: ce n'est PAS la même chose qu'en Java (qui spécifie en fait la précision des bits pour chacun des types 'char', 'byte', 'short', 'int' et 'long').

Kevin S.
la source
la définition de facto de int est qu'il est de 16 bits sur 16 machines et 32 ​​bits sur n'importe quoi de plus grand. Trop de code a été écrit qui suppose que int est de 32 bits de large, pour changer cela maintenant et en conséquence, les gens devraient toujours utiliser size_t ou {, u} int {8,16,32,64} _t s'ils veulent quelque chose de spécifique - - par mesure de précaution, les gens devraient simplement toujours les utiliser, au lieu des types entiers entiers.
Plus clair
3
"Il s'agit d'un entier non signé capable d'exprimer la taille en octets de toute plage de mémoire prise en charge sur la machine hôte." -> No. size_test capable de représenter la taille de n'importe quel objet (par exemple: nombre, tableau, structure). L'ensemble de la plage de mémoire peut dépassersize_t
chux - Réinstallez Monica
"Lors de l'écriture de code C, vous devez toujours utiliser size_t lorsque vous traitez avec des plages de mémoire." - cela implique que chaque index de chaque tableau devrait l'être size_t- j'espère que vous ne le pensez pas. La plupart du temps, nous ne traitons pas de tableaux où la cardinalité de l'espace d'adressage + la portabilité comptent même. Dans ces cas, vous prendriez size_t. Dans tous les autres cas, vous retirez les indices d'entiers (signés). Parce que la confusion (qui vient sans avertissement) provenant d'un comportement de sous-dépassement insoupçonné des non signés est plus courante et pire que les problèmes de portabilité qui peuvent survenir dans les autres cas.
johannes_lalala
23

Le type size_t doit être suffisamment grand pour stocker la taille de tout objet possible. Un entier non signé ne doit pas remplir cette condition.

Par exemple, dans les systèmes 64 bits int et unsigned int peuvent avoir une largeur de 32 bits, mais size_t doit être suffisamment grand pour stocker des nombres supérieurs à 4G

Maciej Hehl
la source
38
"objet" est le langage utilisé par la norme.
R .. GitHub STOP HELPING ICE
2
Je pense que size_tcela ne devrait être aussi gros que si le compilateur pouvait accepter un type X tel que sizeof (X) donnerait une valeur supérieure à 4G. La plupart des compilateurs rejetteraient par exemple typedef unsigned char foo[1000000000000LL][1000000000000LL], et foo[65536][65536];pourraient même être légitimement rejetés s'ils dépassaient une limite documentée définie par l'implémentation.
supercat
1
@MattJoiner: Le libellé est très bien. "Objet" n'est pas vague du tout, mais plutôt défini comme signifiant "région de stockage".
Courses de légèreté en orbite le
4

Cet extrait du manuel glibc 0.02 peut également être pertinent lors de la recherche sur le sujet:

Il existe un problème potentiel avec le type size_t et les versions de GCC avant la version 2.4. ANSI C requiert que size_t soit toujours un type non signé. Pour la compatibilité avec les fichiers d'en-tête des systèmes existants, GCC définit size_t dansstddef.h' to be whatever type the system's sys / types.h 'le définit. La plupart des systèmes Unix qui définissent size_t dans `sys / types.h ', le définissent comme un type signé. Un certain code dans la bibliothèque dépend de la taille de size_t comme un type non signé et ne fonctionnera pas correctement s'il est signé.

Le code de bibliothèque GNU C qui s'attend à ce que size_t ne soit pas signé est correct. La définition de size_t en tant que type signé est incorrecte. Nous prévoyons que dans la version 2.4, GCC définira toujours size_t comme un type non signé, etfixincludes' script will massage the system's sys / types.h 'afin de ne pas entrer en conflit avec cela.

En attendant, nous contournons ce problème en disant explicitement à GCC d'utiliser un type non signé pour size_t lors de la compilation de la bibliothèque GNU C. `configure 'détectera automatiquement le type utilisé par GCC pour size_t afin de le remplacer si nécessaire.

Graeme Burke
la source
3

Si mon compilateur est réglé sur 32 bits, size_tn'est rien d'autre qu'un typedef pour unsigned int. Si mon compilateur est réglé sur 64 bits, size_tn'est rien d'autre qu'un typedef pour unsigned long long.

Poisson zèbre
la source
1
Peut être simplement défini comme unsigned longpour les deux cas sur certains systèmes d'exploitation.
StaceyGirl
-4

size_t est la taille d'un pointeur.

Donc en 32 bits ou le modèle commun ILP32 (entier, long, pointeur) size_t est de 32 bits. et en 64 bits ou le modèle commun LP64 (long, pointeur) size_t est de 64 bits (les entiers sont toujours de 32 bits).

Il existe d'autres modèles mais ce sont ceux que g ++ utilise (au moins par défaut)


la source
15
size_tn'est pas nécessairement de la même taille qu'un pointeur, bien qu'il le soit généralement. Un pointeur doit pouvoir pointer vers n'importe quel emplacement en mémoire; size_tdoit seulement être assez grand pour représenter la taille du plus grand objet unique.
Keith Thompson