Qu'est-ce que size_t en C?


626

Je m'embrouille avec size_ten C. Je sais qu'il est retourné par l' sizeofopérateur. mais qu'est ce que c'est exactement? S'agit-il d'un type de données?

Disons que j'ai une forboucle:

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

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


11
Si ce sont vos seules options, utilisez intsi some_sizeest signé, size_ts'il n'est pas signé.
Nate

8
@Nate C'est incorrect. POSIX a un type ssize_t mais le type réellement correct à utiliser est ptrdiff_t.
Steven Stewart-Gallus

2
Les réponses ne sont pas aussi claires que dans la programmation de bas niveau: C, assemblage et exécution de programme sur Intel® 64 . Comme indiqué dans le livre, l'utilisation d'un index int ipeut ne pas être suffisante pour traiter un énorme tableau. Donc, en utilisant, size_t ivous pouvez adresser plus d'indices, donc même si vous avez un énorme tableau qui ne devrait pas être un problème. size_test un type de données: généralement un unsigned long intmais cela dépend de votre système.
bruno

Réponses:


461

De Wikipédia :

Selon la norme ISO C 1999 (C99), size_test un type entier non signé d'au moins 16 bits (voir 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, ​​définie dans stddef.h. 1 Il peut encore être importé en l'incluant stdlib.hcomme ce fichier en interne comprend stddef.h.

Ce type est utilisé pour représenter la taille d'un objet. 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. De plus, la taille d'opérateur basée sur le compilateur la plus fréquemment utilisée doit être évaluée à une valeur constante compatible avec size_t.

Comme implication, size_test un type garanti pour contenir n'importe quel index de tableau.


4
"Les fonctions de bibliothèque qui prennent ou retournent des tailles s'attendent à ce qu'elles soient de type ... size_t" Sauf que stat () utilise off_t pour la taille d'un fichier
Draemon

64
@Draemon Ce commentaire reflète une confusion fondamentale. size_test pour les objets en mémoire. La norme C ne définit même pas stat()ou off_t(ce sont des définitions POSIX) ou quoi que ce soit à voir avec les disques ou les systèmes de fichiers - elle s'arrête aux FILEflux. La gestion de la mémoire virtuelle est complètement différente des systèmes de fichiers et de la gestion des fichiers en ce qui concerne les exigences de taille, donc la mention off_tn'est pas pertinente ici.
jw013

3
@ jw013: Je dirais à peine que c'est une confusion fondamentale, mais vous soulignez un point intéressant. Pourtant, le texte cité ne dit pas «tailles d'objets en mémoire», et «décalage» n'est guère un bon nom pour un type de taille, quel que soit l'endroit où il se trouve.
Draemon

30
@Draemon Bon point. Cette réponse cite Wikipedia, qui dans ce cas n'a pas la meilleure explication, à mon avis. La norme C elle-même est beaucoup plus claire: elle définit size_tcomme le type du résultat de l' sizeofopérateur (7.17p2 environ <stddef.h>). La section 6.5 explique exactement comment fonctionnent les expressions C (6.5.3.4 pour sizeof). Étant donné que vous ne pouvez pas appliquer sizeofà un fichier disque (principalement parce que C ne définit même pas le fonctionnement des disques et des fichiers), il n'y a pas de place pour la confusion. En d'autres termes, blâmez Wikipédia (et cette réponse pour avoir cité Wikipédia et non la norme C réelle).
jw013

2
@Draemon - Je serais également d'accord avec l'évaluation de la "confusion fondamentale". Si vous n'avez pas lu les normes C / C ++, vous pourriez penser que «objet» fait référence à une «programmation orientée objet», ce qu'il ne fait pas. Lisez la norme C, qui ne contient aucun de ces objets POO, mais qui a pourtant des objets, et découvrez-le. La réponse pourrait te surprendre!
Heath Hunnicutt

220

size_test 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 sûr qu'il ne peut pas être négatif. Par exemple, strlen()renvoie a size_tcar la longueur d'une chaîne doit être d'au moins 0.

Dans votre exemple, si votre index de boucle va 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 size_tobjet, vous devez vous assurer que dans tous les contextes où il est utilisé, 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 des longueurs de str2et str1. Tu ne peux pas faire:

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

En effet, la valeur affectée à diffsera 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 est préférable d'utiliser int(ou long long) pour s1et s2.

Certaines fonctions de C / POSIX pourraient / devraient être utilisées size_t, mais pas pour des raisons historiques. Par exemple, le deuxième paramètre à fgetsdevrait idéalement être size_t, mais il l'est int.


8
@Alok: Deux questions: 1) quelle est la taille de size_t? 2) pourquoi devrais-je préférer size_tquelque chose comme unsigned int?
Lazer

2
@Lazer: la taille de size_test sizeof(size_t). La norme C garantit que ce SIZE_MAXsera au moins 65535. size_test le type retourné par l' sizeofopérateur, et est utilisé dans la bibliothèque standard (par exemple strlenretours size_t). Comme l'a dit Brendan, il size_tn'est pas nécessaire que ce soit le même que unsigned int.
Alok Singhal

4
@Lazer - oui, il size_test garanti qu'il s'agit d'un type non signé.
Alok Singhal

2
@Celeritas non, je veux dire qu'un type non signé ne peut représenter que des valeurs non négatives. J'aurais probablement dû dire "ça ne peut pas représenter des valeurs négatives".
Alok Singhal

4
@JasonOster, le complément à deux n'est pas une exigence dans la norme C. Si la valeur de s2 - s1déborde un int, le comportement n'est pas défini.
Alok Singhal

73

size_t est un type qui peut 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_test défini dans stddef.hma machine:

typedef unsigned long size_t;

4
Cela typedef unsigned long size_tdépend certainement du compilateur. Ou suggérez-vous qu'il en soit toujours ainsi?
chux

4
@chux: En effet, ce n'est pas parce qu'une implémentation le définit comme tel que tout le monde le fait. Exemple: Windows 64 bits. unsigned longest 32 bits, size_test 64 bits.
Tim Čas

2
quel est le but de size_t exactement? Quand je peux me créer une variable comme: "int mysize_t;" ou "long mysize_t" ou "unsigned long mysize_t". Pourquoi quelqu'un aurait-il créé cette variable pour moi?
midkin

1
@midkin size_tn'est pas une variable. C'est un type que vous pouvez utiliser lorsque vous souhaitez représenter la taille d'un objet en mémoire.
Arjun Sreedharan

1
est-il vrai que size_tc'est toujours 32 bits sur une machine 32 bits, 64 bits également?
John Wu

70

Si vous êtes du type empirique ,

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

Sortie pour Ubuntu 14.04 64-bit GCC 4.8:

typedef long unsigned int size_t;

Notez que cela stddef.hest fourni par GCC et non par la glibc src/gcc/ginclude/stddef.hdans GCC 4.2.

Apparitions C99 intéressantes

  • mallocprend size_tcomme argument, il détermine donc la taille maximale qui peut être allouée.

    Et comme il est également renvoyé par sizeof, je pense qu'il limite la taille maximale de tout tableau.

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


1
J'ai le même environnement, cependant, je l'ai testé sur 32 bits, en passant l'option "-m32" du GCC, le résultat était: "typedef unsigned int size_t". Merci d'avoir partagé cette commande géniale @Ciro, cela m'a beaucoup aidé! :-)
silvioprog

2
La question elle-même ne prête pas à confusion. C'est l'esprit confus qui essaie de poser de nombreuses questions et de donner de nombreuses réponses. Je suis surpris que cette réponse et celle d'Arjun Sreedharan n'empêchent toujours pas les gens de demander et de répondre.
biocyberman

1
Très bonne réponse, parce qu'il vous dit en fait ce qui size_test , au moins sur une distro Linux populaire.
Andrey Portnoy


19

Puisque personne ne l'a encore mentionné, la signification linguistique principale de size_test que l' sizeofopérateur renvoie une valeur de ce type. De même, la signification principale de ptrdiff_test 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 cela permettra à ces fonctions de fonctionner avec des objets dont la taille dépasse UINT_MAX sur les systèmes où de tels objets pourraient exister, sans forcer les appelants à perdre du code en passant une valeur supérieure à "unsigned int" sur les systèmes où le type plus grand suffirait pour tous les objets possibles.


Ma question a toujours été: si sizeof n'a jamais existé, y aurait-il besoin de size_t?
Dean P

@DeanP: Peut-être pas, même s'il y aurait alors une question de quel type d'argument devrait être utilisé pour des choses comme malloc(). Personnellement, j'aurais aimé voir des versions qui prennent des arguments de type int, longet long long, avec certaines implémentations favorisant des types plus courts et d'autres implémentant par exemple lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);}[sur certaines plates-formes, appeler à imalloc(123)serait moins cher que d'appeler lmalloc(123);, et même sur une plate-forme où size_test 16 bits, code qui veut allouer une taille calculée dans une valeur "longue" ...
supercat

... devrait pouvoir compter sur l'échec de l'allocation si la valeur est supérieure à ce que l'allocateur peut gérer.
supercat

11

Pour expliquer pourquoi il size_tfallait exister et comment nous en sommes arrivés là:

En termes pragmatiques, size_tet ptrdiff_tsont garantis à 64 bits de large sur une implémentation 64 bits, 32 bits de large sur une implémentation 32 bits, et ainsi de suite. Ils ne pouvaient forcer aucun type existant à signifier cela, sur chaque compilateur, sans casser le code hérité.

Un size_tou ptrdiff_tn'est pas nécessairement le même qu'un intptr_tou uintptr_t. Ils étaient différents sur certaines architectures qui étaient encore en usage quand size_tet ptrdiff_tont été ajoutés à la norme à la fin des années 80, et devenant obsolètes lorsque C99 a ajouté de nombreux nouveaux types mais pas encore disparus (tels que Windows 16 bits). Le x86 en mode protégé 16 bits avait une mémoire segmentée où la plus grande matrice ou structure possible ne pouvait avoir qu'une taille de 65 536 octets, mais un farpointeur devait avoir une largeur de 32 bits, plus large que les registres. Sur ceux-ci, intptr_taurait été de 32 bits de large mais size_tetptrdiff_tpourrait avoir une largeur de 16 bits et rentrer 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 jamais réellement utilisé.

Le type d'un décalage de mémoire ne peut pas être longdû au fait que trop de code hérité suppose une longlargeur exacte de 32 bits. Cette hypothèse a même été intégrée aux API UNIX et Windows. Malheureusement, beaucoup d'autres codes hérités supposaient également que a longest suffisamment large pour contenir un pointeur, un décalage de fichier, le nombre de secondes qui se sont écoulées depuis 1970, etc. POSIX fournit désormais un moyen normalisé de forcer la dernière hypothèse à être vraie au lieu de la première, mais aucune hypothèse portable n'est à faire.

Cela ne pouvait pas être intparce que seule une petite poignée de compilateurs dans les années 90 faisaient int64 bits de large. Ensuite, ils sont vraiment devenus étranges en gardant une longlargeur de 32 bits. La prochaine révision de la norme l'a déclarée illégale pour intêtre plus large que long, mais elle intest toujours de 32 bits sur la plupart des systèmes 64 bits.

Il ne pouvait pas l'être long long int, ce qui a été ajouté de toute façon plus tard, car il a été créé pour être d'au moins 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, c'était de préciser les propriétés qu'un type devait avoir et de ne pas en utiliser une qui signifiait différentes choses dans différents programmes.


Pas d'accord avec " size_tet ptrdiff_tsont garantis à 64 bits de large sur une implémentation 64 bits", etc. La garantie est surestimée. La plage de size_test principalement déterminée par la capacité de mémoire de l'implémentation. "une implémentation de n bits" est principalement la largeur du processeur natif des entiers. De nombreuses implémentations utilisent certainement une taille de mémoire et une largeur de bus de processeur similaires, mais des entiers natifs larges avec une mémoire insuffisante ou des processeurs étroits avec beaucoup de mémoire existent et séparent ces deux propriétés d'implémentation.
chux

8

size_tet intne sont pas interchangeables. Par exemple, sur 64 bits, Linux a une size_ttaille de 64 bits (c'est-à-dire sizeof(void*)) mais intest de 32 bits.

Notez également que ce size_tn'est pas signé. Si vous avez besoin d'une version signée, il y en a ssize_tsur certaines plates-formes et cela serait plus pertinent pour votre exemple.

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


3

En général, si vous commencez à 0 et montez, utilisez toujours un type non signé pour éviter un débordement vous entraînant dans une situation de valeur négative. Ceci est extrêmement important, car si les limites de votre tableau s'avèrent être inférieures au maximum de votre boucle, mais que votre maximum de boucle se trouve être supérieur au maximum de votre type, vous passerez à 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 remontant. Utilisez un non signé.


3
Je ne peux accepter votre argumentation. Vous dites qu'il vaut mieux que le bogue de débordement mène silencieusement à l'accès à des données valides dans votre tableau?
maf-soft

1
@ maf-soft est correct. si l'erreur n'est pas détectée, elle est pire qu'un plantage du programme. pourquoi cette réponse a-t-elle suscité des votes?
yoyo_fun

S'il accède à des données valides dans votre tableau, ce n'est pas un bogue car le type non signé ne débordera pas à la limite du type signé. Quelle est cette logique les gars? Disons que pour une raison quelconque, vous utilisez char pour itérer sur un tableau de 256 éléments ... signé débordera à 127 et 128e élément sera sigsegv, mais si vous utilisez non signé, il parcourra tout le tableau comme prévu. Là encore, lorsque vous utilisez un int, vos tableaux ne seront pas vraiment plus grands que 2 milliards d'éléments, donc dans les deux cas, cela n'a pas d'importance ...
Purple Ice

1
Je ne peux imaginer aucune situation dans laquelle le débordement d'entier n'est pas un bogue, qu'il soit positif ou négatif. Ce n'est pas parce que vous n'obtenez pas de faute de segment que vous voyez un comportement correct! Et vous pouvez rencontrer un défaut de segmentation, ou non, que votre décalage soit positif ou négatif; tout dépend de la configuration de votre mémoire. @PurpleIce, je ne pense pas que vous disiez la même chose que cette réponse; votre argument semble être que vous devez choisir un type de données suffisamment grand pour contenir la plus grande valeur que vous souhaitez y mettre, ce qui est tout simplement du bon sens.
Soren Bjornstad

Cela dit, je préfère utiliser un type non signé pour les index de boucle sémantiquement ; si votre variable ne sera jamais négative, vous pourriez aussi bien l'indiquer dans le type que vous choisissez. Cela pourrait également permettre au compilateur de détecter un bogue où la valeur s'est révélée négative, bien que GCC soit au moins assez terrible pour repérer cette erreur particulière (une fois, j'ai initialisé un signe non signé à -1 et je n'ai pas reçu d'avertissement). De même, un size_t est sémantiquement approprié pour les indices de tableau.
Soren Bjornstad

3

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


1

size_t ou tout type non signé peut être considéré comme une 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 où il est utilisé, 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<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_test un type de données entier non signé qui ne peut affecter que 0 et plus de 0 valeurs entières. Il mesure les octets de la taille de n'importe quel objet et est retourné par l' sizeofopérateur. constest la représentation syntaxique de size_t, mais sans constvous pouvez exécuter le programme.

const size_t number;

size_trégulièrement utilisé pour l'indexation de tableaux et le comptage de boucles. Si le compilateur fonctionne, 32-bitil fonctionnerait unsigned int. Si le compilateur fonctionne, 64-bitil fonctionnerait unsigned long long intégalement. Il existe une taille maximale en size_tfonction du type de compilateur.

size_tdéfinir déjà sur le <stdio.h>fichier d' en- tête, mais il peut également définir par <stddef.h>, <stdlib.h>, <string.h>, <time.h>, les en- <wchar.h>têtes.

  • Exemple (avec 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);
}

Production -: size = 800


  • Exemple (sans 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);
}

Production -: size = 800


-3

D'après ma compréhension, size_test un unsignedentier dont la taille en bits est suffisamment grande pour contenir un pointeur de l'architecture native.

Donc:

sizeof(size_t) >= sizeof(void*)

16
Pas vrai. La taille du pointeur peut être supérieure à size_t. Plusieurs exemples: les compilateurs C en mode réel x86 peuvent avoir 32 bits FARou HUGEpointeurs mais size_t est toujours 16 bits. Autre exemple: Watcom C avait un pointeur spécial pour la mémoire étendue de 48 bits de large, mais ce size_tn'était pas le cas. Sur le contrôleur intégré avec l'architecture Harvard, vous n'avez pas non plus de corrélation, car les deux concernent des espaces d'adressage différents.
Patrick Schlüter

1
Et sur ce stackoverflow.com/questions/1572099/… il y a plus d'exemples AS / 400 avec des pointeurs 128 bits et 32 ​​bitssize_t
Patrick Schlüter

C'est manifestement faux. Cependant, gardons-le ici
Antti Haapala
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.