Tout d'abord: pourquoi ça plante? Nous allons étape par le biais de votre programme comme un débogueur serait.
Note: je vais supposer que votre corps de boucle n'est pas vide, mais accède à la chaîne. Si ce n'est pas le cas, la cause de l'accident est un comportement indéterminé par débordement d'entier. Voir Richard Hansen réponse pour cela.
std::string s = "aa";//assign the two-character string "aa" to variable s of type std::string
for ( int i = 0; // create a variable i of type int with initial value 0
i < s.length() - 3 // call s.length(), subtract 3, compare the result with i. OK!
{...} // execute loop body
i++ // do the incrementing part of the loop, i now holds value 1!
i < s.length() - 3 // call s.length(), subtract 3, compare the result with i. OK!
{...} // execute loop body
i++ // do the incrementing part of the loop, i now holds value 2!
i < s.length() - 3 // call s.length(), subtract 3, compare the result with i. OK!
{...} // execute loop body
i++ // do the incrementing part of the loop, i now holds value 3!
.
.
Nous attendons de la case i < s.length() - 3
à l'échec tout de suite, puisque la longueur d' s
est de deux (nous n'tous donné une longueur au début et n'a jamais changé) et 2 - 3
est -1
, 0 < -1
est faux. Cependant, nous ne pouvons obtenir un "OK" ici.
C'est parce qu' s.length()
n'est pas 2
. Il est 2u
. std::string::length()
a type de retour size_t
qui est un entier non signé. Pour en revenir à la condition de la boucle, nous avons d'abord obtenir la valeur de s.length()
, alors 2u
, maintenant soustraire 3
. 3
est un entier littéral et interprété par le compilateur comme type int
. Ainsi, le compilateur doit calculer 2u - 3
, deux des valeurs de types différents. Les opérations sur les types primitifs ne fonctionnent pour les mêmes types, donc on doit être converti dans l'autre. Il existe des règles strictes, dans ce cas, unsigned
"gagne", alors 3
est converti 3u
. Dans des entiers non signés, 2u - 3u
ne peut pas être -1u
que ce nombre n'existe pas (bien, parce que c'est un signe bien sûr!). Au lieu de cela, il calcule chaque opération modulo 2^(n_bits)
où n_bits
est le nombre de bits dans ce type (généralement de 8, 16, 32 ou 64). Ainsi, au lieu de -1
nous obtenons 4294967295u
(en supposant 32 bits).
Alors maintenant, le compilateur est fait avec s.length() - 3
(bien sûr, il est beaucoup beaucoup plus rapide que moi ;-) ), passons maintenant à la comparaison: i < s.length() - 3
. Mettre dans les valeurs: 0 < 4294967295u
. Encore une fois, les différents types, 0
devient 0u
, la comparaison 0u < 4294967295u
est évidemment vrai, la condition de la boucle est positivement vérifiée, nous pouvons maintenant exécuter le corps de la boucle.
Après l'incrémentation, la seule chose qui change dans le ci-dessus est la valeur de i
. La valeur de i
sera de nouveau converti en un unsigned int, comme la comparaison des besoins.
Nous avons donc
(0u < 4294967295u) == true, let's do the loop body!
(1u < 4294967295u) == true, let's do the loop body!
(2u < 4294967295u) == true, let's do the loop body!
Voici le problème: Que faites-vous dans le corps de la boucle? Sans doute vous avez accès à l' i^th
caractère de votre chaîne, n'est-ce pas? Même si ce n'était pas votre intention, vous n'avez pas seulement consulté le zéro et d'abord, mais aussi le second! Le second n'existe pas (en tant que votre chaîne ne dispose que de deux caractères, le zéro et le premier), vous avez accès à la mémoire, vous ne devriez pas, le programme fait ce qu'il veut (comportement indéfini). À noter que le programme n'est pas nécessaire de crash immédiatement. Il semble bien fonctionner pour une autre demi-heure, de sorte que ces erreurs sont difficiles à attraper. Mais il est toujours dangereux d'accéder à la mémoire au-delà des limites, c'est là que la plupart des accidents viennent.
Donc en résumé, vous obtenez une valeur différente de s.length() - 3
que ce que vous attendez, il en résulte un positif de la boucle de contrôle de condition, qui conduit à répétitif de l'exécution du corps de la boucle, ce qui en soi accède à la mémoire, il ne devrait pas.
Maintenant, nous allons voir comment éviter que, soit la façon de dire au compilateur que vous avez fait dans votre condition de boucle.
Longueurs de chaînes et les tailles de conteneurs sont intrinsèquement non signé , donc vous devez utiliser un entier non signé dans les boucles for.
Depuis unsigned int
est assez long et donc pas souhaitable d'écrire encore et encore, en boucle, il suffit d'utiliser size_t
. C'est le type de tous les conteneurs de la STL utilise pour stocker la longueur ou la taille. Vous devrez peut-être inclure cstddef
à affirmer l'indépendance de plate-forme.
#include <cstddef>
#include <string>
using namespace std;
int main() {
string s = "aa";
for ( size_t i = 0; i + 3 < s.length(); i++) {
// ^^^^^^ ^^^^
}
}
Depuis a < b - 3
est mathématiquement équivalente à a + 3 < b
, nous pouvons les échange. Toutefois, a + 3 < b
empêche b - 3
à être d'une grande valeur. Rappelons qu' s.length()
renvoie un entier non signé et non signé entiers effectuer des opérations de module 2^(bits)
où les bits est le nombre de bits dans le type (généralement de 8, 16, 32 ou 64). Donc avec s.length() == 2
, s.length() - 3 == -1 == 2^(bits) - 1
.
Alternativement, si vous souhaitez utiliser i < s.length() - 3
pour la préférence personnelle, vous devez ajouter une condition:
for ( size_t i = 0; (s.length() > 3) && (i < s.length() - 3); ++i )
// ^ ^ ^- your actual condition
// ^ ^- check if the string is long enough
// ^- still prefer unsigned types!