web-dev-qa-db-fra.com

Devrais-je utiliser std :: size_t ou int dans mes boucles for?

Je me demande juste si je devrais utiliser std::size_t pour les boucles et le matériel 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 pour savoir quand utiliser std::size_t?

181
nhaa123

Une bonne règle générale s'applique à tout ce que vous devez comparer dans l'état de boucle à un élément qui est naturellement un std::size_t lui-même.

std::size_t est le type de toute expression sizeof et il est garanti qu’il est possible d’exprimer la taille maximale de n’importe quel objet (y compris tout tableau) en C++. Par extension, il est également garanti que sa taille est suffisante pour tout index de tableau. Il s'agit donc d'un type naturel pour une boucle indexée sur un tableau.

Si vous comptez jusqu’à un nombre, il peut être plus naturel d’utiliser le type de la variable qui contient ce nombre ou un int ou unsigned int (si elle est suffisamment grande) car elles doivent avoir une taille naturelle pour la machine.

168
CB Bailey

size_t est le type de résultat de l'opérateur sizeof.

Utilisation size_t pour les variables qui modélisent la taille ou l’index dans un tableau. size_t exprime la sémantique: vous savez immédiatement qu’elle représente une taille en octets ou en index, plutôt qu’un autre entier.

De plus, en utilisant size_t représenter une taille en octets aide à rendre le code portable.

69
Gregory Pakosz

Le size_t type est destiné à spécifier la taille de quelque chose, il est donc naturel de l'utiliser, par exemple, obtenir la longueur d'une chaîne, puis traiter chaque caractère:

for (size_t i = 0, max = strlen (str); i < max; i++)
    doSomethingWith (str[i]);

Vous do devez vous méfier des conditions aux limites, car il s'agit d'un type non signé. La limite en haut n’est généralement pas très importante car le maximum est généralement grand (même s’il est est possible de l’atteindre). La plupart des gens utilisent simplement un int pour ce genre de chose car ils ont rarement des structures ou des tableaux assez grands pour dépasser la capacité de ce int.

Mais faites attention aux choses comme:

for (size_t i = strlen (str) - 1; i >= 0; i--)

ce qui causera une boucle infinie à cause du comportement de retour à la ligne des valeurs non signées (même si 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 immunisé contre les problèmes d’emballage):

for (size_t i = strlen (str); i-- > 0; )

En décalant la décrémentation en un effet secondaire post-vérification de la condition de continuation, la vérification de la poursuite de la valeur before décrémentée, mais utilise toujours la valeur décrémentée à l'intérieur de la boucle la boucle court de len .. 1 plutôt que len-1 .. 0).

30
paxdiablo

Par définition, size_t est le résultat de l'opérateur sizeof. 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) n’est pas une question de taille, alors pourquoi utiliser size_t? int ou unsigned int, ça devrait être bon.

Bien entendu, ce que vous faites avec i dans la boucle est également pertinent. Si vous le passez à une fonction qui prend un unsigned int, par exemple, choisissez unsigned int.

Dans tous les cas, je recommande d'éviter les conversions de type implicites. Rendre toutes les conversions de types explicites.

12
Daniel Daranas

size_t est un moyen très lisible de spécifier la dimension de la taille d’un élément - longueur d’une chaîne, nombre d’octets utilisés par un pointeur, etc. et size_t - quelque chose que unsigned int pourrait ne pas le faire (par exemple, quand utiliser unsigned long

10
Ofir

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 indexer et compter les éléments vectoriels.

En pratique, il s’agit généralement d’entités non signées, mais cela n’est pas garanti, en particulier lors de l’utilisation d’allocateurs personnalisés.

8
Peter Alexander

Bientôt, la plupart des ordinateurs seront des architectures 64 bits avec un système d’exploitation 64 bits: ils exécutent des programmes fonctionnant sur des conteneurs de milliards d’éléments. Alors vous devez utiliser size_t au lieu de int en tant qu’index de boucle, sinon votre index sera renvoyé à l’envers à l’élément 2 ^ 32: ème, sur les systèmes 32 et 64 bits.

Préparez-vous pour l'avenir!

8
Nordlöw

réponse courte:

presque jamais

longue réponse:

Chaque fois que vous avez besoin d'un vecteur de caractère supérieur à 2 Go sur un système 32 bits. Dans tous les autres cas d'utilisation, utiliser un type signé est beaucoup plus sûr que d'utiliser un type non signé.

exemple:

std::vector<A> data;
[...]
// calculate the index that should be used;
size_t i = calc_index(param1, param2);
// doing calculations close to the underflow of an integer is already dangerous

// do some bounds checking
if( i - 1 < 0 ) {
    // always false, because 0-1 on unsigned creates an underflow
    return LEFT_BORDER;
} else if( i >= data.size() - 1 ) {
    // if i already had an underflow, this becomes true
    return RIGHT_BORDER;
}

// now you have a bug that is very hard to track, because you never 
// get an exception or anything anymore, to detect that you actually 
// return the false border case.

return calc_something(data[i-1], data[i], data[i+1]);

L'équivalent signé de size_t est ptrdiff_t, pas int. Mais utiliser int est toujours bien meilleur que size_t. ptrdiff_t est long sur des systèmes 32 et 64 bits.

Cela signifie que vous devez toujours convertir depuis et vers 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 concevoir std :: vector avec un size_t non signé était une erreur.

Si votre compilateur vous avertit des conversions implicites de ptrdiff_t en size_t, vous pouvez le rendre explicite à l'aide de la syntaxe du constructeur:

calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);

si vous voulez juste itérer une collection, sans limite de contrôle, utilisez la plage basée sur:

for(const auto& d : data) {
    [...]
}

voici quelques mots de Bjarne Stroustrup (auteur C++) at en natif

Pour certaines personnes, cette erreur de conception signé/non signé dans la STL est une raison suffisante pour ne pas utiliser le vecteur std ::, mais plutôt une implémentation propre.

7
Arne

Lorsque vous utilisez size_t, faites attention à l'expression suivante

size_t i = containner.find("mytoken");
size_t x = 99;
if (i-x>-1 && i+x < containner.size()) {
    cout << containner[i-x] << " " << containner[i+x] << endl;
}

Vous obtiendrez false dans l'expression if, quelle que soit la valeur que vous avez pour x. Il m'a fallu plusieurs jours pour réaliser cela (le code est si simple que je n'ai pas fait de test unitaire), bien que cela ne prenne que quelques minutes pour comprendre la source du problème. Pas sûr qu'il soit préférable de faire un casting ou d'utiliser zéro.

if ((int)(i-x) > -1 or (i-x) >= 0)

Les deux manières devraient fonctionner. Voici mon essai

size_t i = 5;
cerr << "i-7=" << i-7 << " (int)(i-7)=" << (int)(i-7) << endl;

La sortie: i-7 = 18446744073709551614 (int) (i-7) = - 2

J'aimerais avoir les commentaires des autres.

4
Kemin Zhou

size_t est renvoyé par diverses bibliothèques pour indiquer que la taille de ce conteneur est différente de zéro. Vous l'utilisez quand vous revenez une fois: 0

Cependant, dans l'exemple ci-dessus, boucler sur un size_t est un bogue potentiel. Considérer ce qui suit:

for (size_t i = thing.size(); i >= 0; --i) {
  // this will never terminate because size_t is a typedef for
  // unsigned int which can not be negative by definition
  // therefore i will always be >= 0
  printf("the never ending story. la la la la");
}

l'utilisation d'entiers non signés peut potentiellement créer ce type de problèmes subtils. Par conséquent, je préfère utiliser size_t uniquement lorsque j'interagis avec les conteneurs/types qui en ont besoin.

2
ascotan