Question

examiner le programme ci-dessous

    char str[5];
    strcpy(str,"Hello12345678");
    printf("%s",str);

Lorsqu'il est exécuté, ce programme donne une erreur de segmentation.

Mais quand strcpy est remplacé par la suite, le programme fonctionne très bien.

strcpy(str,"Hello1234567");

question devrait planter lorsque vous essayez de copier str toute autre chaîne de longueur plus de 5 caractères.

Alors pourquoi il ne s'écraser pour « Hello1234567 » et ne s'écraser pour « Hello12345678 » dire de chaîne de longueur 13 ou plus de 13.

Ce programme a été exécuté sur 32 bit machine.

Était-ce utile?

La solution

Il existe trois types de comportement des normes que vous devriez être intéressé par.

1 / défini par le comportement . Cela fonctionne sur toutes les implémentations conformes. Utilisez cette librement.

2 / comportement défini par l'implémentation . Comme il est indiqué, cela dépend de la mise en œuvre, mais au moins il est encore défini. Implémentations sont tenus de documenter ce qu'ils font dans ces cas. Utilisez cette option si vous ne se soucient pas de la portabilité.

3 / comportement non défini . Tout peut arriver. Et nous voulons dire quoi que ce soit , jusqu'à et y compris l'ensemble de votre ordinateur s'effondrer dans une singularité nue et s'avaler, vous et une grande partie de vos collègues de travail. Ne jamais utiliser. Déjà! Sérieusement! Ne me faites pas venir là-bas.

Copie de plus de 4 caractères et d'un zéro octet à un char[5] est un comportement non défini.

Sérieusement, peu importe pourquoi votre programme se bloque avec 14 caractères, mais pas 13, vous êtes certainement écraser des informations non-crash sur la pile et votre programme sera plus susceptible de produire des résultats incorrects de toute façon. En fait, l'accident est mieux depuis au moins il vous empêche compter sur les effets éventuellement négatifs.

Augmenter la taille du tableau à quelque chose de plus approprié (char[14] dans ce cas, les informations disponibles) ou utiliser une autre structure de données qui peuvent faire face.


Mise à jour:

Puisque vous semblez si préoccupé de trouver pourquoi un 7 caractères supplémentaires ne provoque pas de problèmes, mais 8 caractères ne, nous allons envisager la possible mise en page de la pile en entrant main(). Je dis « possible » depuis la mise en page réelle dépend de la convention d'appel que votre compilateur utilise. Étant donné que le code de démarrage C par téléphone main() avec argc et argv, la pile au début de main(), après attribution d'espace pour un char[5], pourrait ressembler à ceci:

+------------------------------------+
| C start-up code return address (4) |
| argc (4)                           |
| argv (4)                           |
| x = char[5] (5)                    |
+------------------------------------+

Lorsque vous rédigez le Hello1234567\0 d'octets avec:

strcpy (x, "Hello1234567");

à x, elle écrasera la argc et argv mais, au retour de main(), c'est correct. Plus précisément Hello x remplit, 1234 argv et 567\0 remplit argc remplit. Pourvu que vous n'essayez pas réellement à utiliser argc et / ou argv après cela, vous serez d'accord:

+------------------------------------+ Overwrites with:
| C start-up code return address (4) |
| argc (4)                           |   '567<NUL>'
| argv (4)                           |   '1234'
| x = char[5] (5)                    |   'Hello'
+------------------------------------+

Cependant, si vous écrivez Hello12345678\0 (notez l'extra « 8 ») à x, elle écrasera la argc et argv et aussi un octet de l'adresse de retour de sorte que, lorsque main() tente de revenir à le code C de démarrage, il en va dans la terre de fées à la place:

+------------------------------------+ Overwrites with:
| C start-up code return address (4) |   '<NUL>'
| argc (4)                           |   '5678'
| argv (4)                           |   '1234'
| x = char[5] (5)                    |   'Hello'
+------------------------------------+

Encore une fois, cela dépend entièrement de la convention d'appel de votre compilateur. Il est possible un compilateur différent serait toujours Matelasser des tableaux à un multiple de 4 octets et le code ne manquerait pas là jusqu'à ce que vous avez écrit trois autres personnages. Même le même compilateur peut affecter les variables sur le cadre de la pile différemment pour assurer l'alignement est satisfaite.

C'est ce qu'ils veulent dire par indéfini: vous ne pas savent Qu'est-ce qui va se passer

.

Autres conseils

Vous copiez la pile, il est donc dépendant de ce que le compilateur a placé sur la pile, pour la quantité de données supplémentaires seront nécessaires pour planter votre programme.

Certains compilateurs peuvent produire un code qui va se planter avec un seul octet sur la taille de la mémoire tampon - c'est ce que le comportement est indéfini

.

Je suppose que la taille 13 est suffisant pour remplacer l'adresse de retour, ou quelque chose de similaire, qui se bloque lorsque votre fonction retourne. Mais un autre compilateur ou une autre plate-forme pourrait / se planter avec une longueur différente.

Aussi votre programme peut se bloquer avec une longueur différente si elle a duré plus longtemps, si quelque chose était écrasé moins important.

Pour Intel plate-forme 32 bits l'explication est la suivante. Lorsque vous déclarez char [5] sur pile le compilateur alloue vraiment 8 octets en raison de l'alignement. Ensuite, il est typique pour les fonctions d'avoir le prologue suivant:

push ebp
mov ebp, esp

cela permet d'économiser la valeur de registre ebp sur la pile, puis se déplace registre esp valeur en ebp pour l'utilisation de la valeur esp pour accéder aux paramètres. Cela conduit à 4 octets plus sur la pile à occuper une valeur ebp.

Dans le ebp épilogue est restauré, mais sa valeur est généralement utilisé que pour accéder à des paramètres de fonction allouée-pile, donc il ne peut pas écraser du mal dans la plupart des cas.

Vous avez donc la mise en page suivante (pile grossit vers le bas sur Intel). 8 octets pour votre tableau, puis 4 octets pour ebp, puis généralement l'adresse de retour

Ceci est la raison pour laquelle vous devez écraser au moins 13 octets pour planter votre programme.

Pour ajouter aux réponses ci-dessus: vous pouvez tester les bugs comme ceux-ci avec un outil tel que Valgrind . Si vous êtes sous Windows, consultez la page ce sujet SO .

Cela dépend de ce qui est sur la pile après le tableau « str ». Vous venez tout juste de ne pas être piétiner quoi que ce soit critique jusqu'à ce que vous copiez que beaucoup de caractères.

Alors il va dépendre de ce que le reste est en fonction, le compilateur que vous utilisez et peut-être les options du compilateur aussi.

13 est 5 + 8, ce qui suggère qu'il ya deux mots non critiques après le tableau de str, alors quelque chose de critique (peut-être l'adresse de retour)

C'est la beauté pure du comportement non défini (UB): il est indéfini

.

Votre code:

char str[5];
strcpy(str,"Hello12345678");

écrit 14 octets / caractères à str qui ne peut contenir 5 octets / caractères. Cela appelle UB.

Q:. Alors pourquoi il ne s'écraser pour « Hello1234567 » et ne s'écraser pour « Hello12345678 » dire de chaîne de longueur 13 ou plus de 13

  

Parce que le comportement est indéfini.   Utilisez strncpy. Voir cette page    http://en.wikipedia.org/wiki/Strcpy   pour plus d'informations.

strncpy est dangereux car il n'ajoute une terminaison NULL si la chaîne source a une longueur> = n où n est la taille de la chaîne de destination.

char s[5];
strncpy(s,5,"test12345");
printf("%s",s); // crash

Nous utilisons toujours pour atténuer ce strlcpy.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top