Je me demande simplement si je devrais utiliser std::size_t
des boucles et d'autres choses au lieu de int
? Par exemple:
#include <cstdint>
int main()
{
for (std::size_t i = 0; i < 10; ++i) {
// std::size_t OK here? Or should I use, say, unsigned int instead?
}
}
En général, quelle est la meilleure pratique concernant le moment de l'utilisation std::size_t
?
size_t
quand vous le devriez peut entraîner des bogues de sécurité .ssize_t
pour les valeurs signées.size_t
est le type de résultat de l'sizeof
opérateur.À utiliser
size_t
pour les variables qui modélisent la taille ou l'index dans un tableau.size_t
transmet la sémantique: vous savez immédiatement qu'elle représente une taille en octets ou un index, plutôt qu'un simple autre entier.De plus, utiliser
size_t
pour représenter une taille en octets aide à rendre le code portable.la source
Le
size_t
type est destiné à spécifier la taille de quelque chose, il est donc naturel de l'utiliser, par exemple, pour obtenir la longueur d'une chaîne, puis pour traiter chaque caractère:Vous ne devez surveiller les conditions aux limites bien sûr, car il est un type non signé. La limite à l'extrémité supérieure n'est généralement pas si importante car le maximum est généralement grand (bien qu'il soit possible de s'y rendre). La plupart des gens utilisent juste un
int
pour ce genre de chose car ils ont rarement des structures ou des tableaux qui deviennent suffisamment grands pour dépasser la capacité de celaint
.Mais attention aux choses comme:
ce qui provoquera une boucle infinie en raison du comportement d'habillage des valeurs non signées (bien que j'ai vu des compilateurs mettre en garde contre cela). Cela peut également être atténué par le (légèrement plus difficile à comprendre mais au moins à l'abri des problèmes d'emballage):
En décalant la décrémentation dans un effet secondaire post-vérification de la condition de continuation, cela vérifie la continuation de la valeur avant décrémentation, mais utilise toujours la valeur décrémentée à l'intérieur de la boucle (c'est pourquoi la boucle s'exécute
len .. 1
plutôt quelen-1 .. 0
).la source
strlen
à chaque itération d'une boucle. :) Vous pouvez faire quelque chose comme ça:for (size_t i = 0, len = strlen(str); i < len; i++) ...
for (size_t i = strlen (str); i --> 0;)
-->
opérateur "va à" (voir stackoverflow.com/questions/1642028/… ). Vous avez intégré votre suggestion dans la réponse.if (i == 0) break;
à la fin de la boucle for (par exemple,for (size_t i = strlen(str) - 1; ; --i)
(j'aime mieux le vôtre, mais je me demande simplement si cela fonctionnerait aussi bien).Par définition,
size_t
est le résultat de l'sizeof
opérateur.size_t
a été créé pour faire référence aux tailles.Le nombre de fois que vous faites quelque chose (10, dans votre exemple) ne concerne pas les tailles, alors pourquoi l'utiliser
size_t
?int
, ouunsigned int
, ça devrait aller.Bien sûr, il est également pertinent de savoir ce que vous faites à l'
i
intérieur de la boucle. Si vous le passez à une fonction qui prendunsigned int
, par exemple, uneunsigned int
.Dans tous les cas, je recommande d'éviter les conversions de types implicites. Rendez toutes les conversions de types explicites.
la source
size_t
est une façon très lisible pour spécifier la dimension de la taille d'un élément - longueur d'une chaîne, la quantité d'octets un pointeur prend, etc. Il est également portable sur toutes les plateformes - vous constaterez que 64bit et 32bit deux se comportent bien avec les fonctions du système etsize_t
- quelque chose quiunsigned int
pourrait ne pas faire (par exemple, quand devriez-vous utiliserunsigned long
la source
réponse courte:
presque jamais
longue réponse:
Chaque fois que vous avez besoin d'un vecteur de caractères supérieur à 2 Go sur un système 32 bits. Dans tous les autres cas d'utilisation, l'utilisation d'un type signé est beaucoup plus sûre que l'utilisation d'un type non signé.
exemple:
L'équivalent signé de
size_t
estptrdiff_t
, nonint
. Mais l'utilisationint
est toujours bien meilleure dans la plupart des cas que size_t.ptrdiff_t
estlong
sur les systèmes 32 et 64 bits.Cela signifie que vous devez toujours convertir vers et depuis size_t chaque fois que vous interagissez avec un conteneur std ::, ce qui n'est pas très beau. Mais lors d'une conférence native en cours, les auteurs de c ++ ont mentionné que la conception de std :: vector avec un size_t non signé était une erreur.
Si votre compilateur vous donne des avertissements sur les conversions implicites de ptrdiff_t à size_t, vous pouvez le rendre explicite avec la syntaxe du constructeur:
si vous voulez simplement itérer une collection, sans limites, utilisez la plage basée sur:
voici quelques mots de Bjarne Stroustrup (auteur C ++) à devenir natif
Pour certaines personnes, cette erreur de conception signée / non signée dans la STL est une raison suffisante pour ne pas utiliser le vecteur std ::, mais plutôt une implémentation propre.
la source
for(int i = 0; i < get_size_of_stuff(); i++)
. Maintenant, bien sûr, vous ne voudrez peut-être pas faire beaucoup de boucles brutes, mais - allez, vous les utilisez aussi.x + 1 < y
équivaut àx < y - 1
, mais ils ne sont pas avec des entiers non-tendeurs. Cela peut facilement introduire des bogues lorsque des choses sont transformées et supposées équivalentes.Utilisez std :: size_t pour indexer / compter les tableaux de style C.
Pour les conteneurs STL, vous aurez (par exemple)
vector<int>::size_type
, qui devrait être utilisé pour l'indexation et le comptage des éléments vectoriels.En pratique, il s'agit généralement de deux entiers non signés, mais cela n'est pas garanti, en particulier lors de l'utilisation d'allocateurs personnalisés.
la source
std::size_t
est généralementunsigned long
(8 octets sur les systèmes 64 bits) plutôt queunisgned int
(4 octets).size_t
, car les index peuvent être négatifs. Cependant, on pourrait utilisersize_t
sa propre instance d'un tel tableau si l'on ne veut pas devenir négatif.+
sur des pointeurs, il semblerait que ceptrdiff_t
soit celui à utiliser pour les index.vector<T>::size_type
(et idem pour tous les autres conteneurs), il est en fait plutôt inutile, car il est effectivement garanti d'êtresize_t
- il est typéAllocator::size_type
et, pour les restrictions concernant les conteneurs, voir 20.1.5 / 4 - en particulier,size_type
doit êtresize_t
etdifference_type
doit êtreptrdiff_t
. Bien sûr, la valeur par défautstd::allocator<T>
satisfait ces exigences. Alors utilisez simplement le plus courtsize_t
et ne vous embêtez pas avec le reste du lot :)Bientôt, la plupart des ordinateurs seront des architectures 64 bits avec un système d'exploitation 64 bits: exécutant des programmes fonctionnant sur des conteneurs de milliards d'éléments. Ensuite, vous devez utiliser
size_t
au lieu d'int
un index de boucle, sinon votre index se terminera à l'élément 2 ^ 32: th, sur les systèmes 32 et 64 bits.Préparez-vous pour l'avenir!
la source
long int
plutôt que d'unint
. Sisize_t
est pertinent sur un système d'exploitation 64 bits, il était tout aussi pertinent sur un système d'exploitation 32 bits.Lorsque vous utilisez size_t, soyez prudent avec l'expression suivante
Vous obtiendrez faux dans l'expression if, quelle que soit la valeur que vous avez pour x. Il m'a fallu plusieurs jours pour m'en rendre compte (le code est si simple que je n'ai pas fait de test unitaire), même si cela ne prend que quelques minutes pour comprendre la source du problème. Pas sûr qu'il vaut mieux faire un cast ou utiliser zéro.
Les deux façons devraient fonctionner. Voici mon test
La sortie: i-7 = 18446744073709551614 (int) (i-7) = - 2
J'aimerais les commentaires des autres.
la source
(int)(i - 7)
s'agit d'un sous-dépassement qui est converti par laint
suite, alors qu'ilint(i) - 7
ne s'agit pas d'un sous-dépassement puisque vous convertissez d'abordi
en unint
, puis soustrayez7
. De plus, j'ai trouvé votre exemple déroutant.size_t est renvoyé par diverses bibliothèques pour indiquer que la taille de ce conteneur n'est pas nulle. Vous l'utilisez à votre retour: 0
Cependant, dans votre exemple ci-dessus, une boucle sur un size_t est un bogue potentiel. Considérer ce qui suit:
l'utilisation d'entiers non signés a le potentiel de créer ces types de problèmes subtils. Par conséquent, à mon humble avis, je préfère utiliser size_t uniquement lorsque j'interagis avec les conteneurs / types qui en ont besoin.
la source
size_t
est un type non signé qui peut contenir une valeur entière maximale pour votre architecture, il est donc protégé contre les débordements d'entiers dus à la signature (signé int0x7FFFFFFF
incrémenté de 1 vous donnera -1) ou à la taille courte (unsigned short int 0xFFFF incrémenté de 1 vous donnera 0).Il est principalement utilisé dans l'indexation de tableaux / boucles / arithmétiques d'adresses, etc. Les fonctions similaires
memset()
et similaires acceptentsize_t
uniquement, car théoriquement, vous pouvez avoir un bloc de mémoire de taille2^32-1
(sur une plate-forme 32 bits).Pour de telles boucles simples, ne vous embêtez pas et utilisez simplement int.
la source
size_t est un type intégral non signé, qui peut représenter le plus grand entier sur votre système. Utilisez-le uniquement si vous avez besoin de très grands tableaux, matrices, etc.
Certaines fonctions renvoient un size_t et votre compilateur vous avertira si vous essayez de faire des comparaisons.
Évitez cela en utilisant un type de données signé / non signé approprié ou simplement transtypé pour un hack rapide.
la source
size_t n'est pas signé int. donc chaque fois que vous voulez un entier non signé, vous pouvez l'utiliser.
Je l'utilise quand je veux spécifier la taille du tableau, le compteur ect ...
la source
size_t
peut s'agir d'un entier 64 bits non signé, tandis que sur une machine 32 bits, il ne s'agit que d'un entier non signé 32 bits.