Comment strcpy fonctionne en arrière-plan?
Cela peut être une question très simple pour certains. J'essayais de comprendre comment la fonction strcpy fonctionne réellement derrière les coulisses. par exemple, dans ce code
#include <stdio.h>
#include <string.h>
int main ()
{
char s[6] = "Hello";
char a[20] = "world isnsadsdas";
strcpy(s,a);
printf("%s\n",s);
printf("%d\n", sizeof(s));
return 0;
}
Que je suis déclarant s
un tableau statique de taille inférieure à celle de la source. J'ai pensé qu'il l'habitude d'imprimer le mot en entier, mais il n'a imprimer world isnsadsdas
.. Donc, je pensais que cette fonction strcpy peut-être de l'attribution de nouveaux taille si la destination est inférieur à la source. Mais maintenant, quand je vérifie sizeof(s), il est encore 6, mais c'est l'impression plus que cela. Comment que travailler réellement?
- Peut-être que c'utilisation
memcpy
? - Attendez... ce que je reçois est
"world isnsadsdas"
. Il n'y a pas de"Hello"
. - en fait c'est que seulement.. par erreur j'ai ajouté bonjour.. encore dépasser la taille
- étudiant.cs.uwaterloo.ca/~cs350/common/os161-src-html/... Avis il n'y a pas d'allocation de mémoire. Aussi, vous trouverez peut-être que si vous imprimez 'a' après le strcpy, il est foiré, mais aussi pas de promesses, parce que le compilateur pourrait allouer plus d'espace que vous avez demandé.
- Strcpy copie DE a À s dans votre exemple. Donc vous avez un débordement de la pile, puisque vous êtes de mettre l'ensemble de la chaîne "monde isnsadadas" dans un tampon de longueur de 6 octets.
Vous devez vous connecter pour publier un commentaire.
Vous avez causé un comportement indéterminé, alors tout peut arriver. Dans votre cas, vous avez de la chance et il n'est pas en panne, mais vous ne devriez pas compter sur ce qui se passe. Voici une procédure simplifiée
strcpy
de mise en œuvre (mais c'est pas trop loin de la de nombreux les vrais):sizeof
est juste de rentrer, vous la taille de votre tableau de compilation. Si vous utilisezstrlen
, je pense que vous verrez ce que vous attendez. Mais comme je l'ai mentionné ci-dessus, en s'appuyant sur un comportement indéterminé est une mauvaise idée.strcpy
sémantique.strcpy
plus. C'est plus commestrncpy
, mais avec construit-dansstrlen(dest)
plutôt que de lan
paramètre. Edit: Attendez, où est-il allé?http://natashenka.ca/wp-content/uploads/2014/01/strcpy8x11.png
strcpy est considéré comme dangereux pour des raisons comme celle que vous démontrez. Les deux tampons que vous avez créé sont des variables locales stockées dans le cadre de la pile de la fonction. Voici à peu près ce que le frame de pile ressemble à:
http://upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Call_stack_layout.svg/342px-Call_stack_layout.svg.png
Pour info, les choses sont mis sur le dessus de la pile sens il pousse vers l'arrière à travers la mémoire (Ce qui ne signifie pas que les variables en mémoire lu à l'envers, c'est juste que les plus récents sont mis derrière les plus anciennes). Cela signifie donc que si vous écrivez assez loin dans les locaux de la section de votre fonction de cadre de pile, vous allez écrire sur tous les autres de la pile variable après la variable à copier et de percer dans d'autres sections, et éventuellement remplacer le pointeur de retour. Le résultat est que si vous êtes intelligent, vous avez le plein contrôle de l'endroit où la fonction retourne. Vous pourriez faire n'importe quoi vraiment, mais ce n'est pas VOUS qui les concernent.
Comme vous semblez le savoir en faisant votre premier tampon 6 caractères de long pour 5 de chaîne de caractères, chaînes C fin dans un octet null \x00. La fonction strcpy copie octets jusqu'à la source octet est 0, mais il ne vérifie pas que la destination est long, c'est pourquoi il peut copier sur la limite de la matrice. C'est aussi pourquoi votre impression est la lecture de la mémoire tampon passé sa taille, il lit jusqu' \x00. Fait intéressant, la fonction strcpy pu écrire dans les données de la s en fonction de l'ordre du compilateur l'a remis dans la pile, donc un exercice amusant pourrait être également imprimer un et voir si vous obtenez quelque chose comme "snsadsdas", mais je ne peux pas être sûr de ce qu'il pourrait ressembler, même si c'est polluant s car il y a parfois des octets entre la pile des entrées pour des raisons différentes).
Si ce tampon détient dire, un mot de passe pour vérifier dans le code avec une fonction de hachage, et vous le copier à un tampon dans la pile à partir de là où vous l'obtenir (un paquet réseau si un serveur, ou une zone de texte, etc), vous peut très bien copier plus de données à partir de la source à la destination de la mémoire tampon peut contenir et donner de contrôle de retour de votre programme de façon à ce que l'utilisateur est en mesure d'envoyer un paquet à vous ou à essayer un mot de passe. Ils ont juste à taper le bon nombre de caractères et les caractères corrects qui représentent une adresse quelque part dans la mémoire ram à sauter.
Vous pouvez utiliser la fonction strcpy si vous vérifiez les limites et peut-être couper la source de la chaîne, mais il est considéré comme une mauvaise pratique. Il y a de plus moderne des fonctions qui prennent un max de longueur comme http://www.cplusplus.com/reference/cstring/strncpy/
Oh, et enfin, tout ceci est appelé un dépassement de la mémoire tampon. Certains compilateurs ajouter une belle petite goutte d'octets choisi de façon aléatoire par le système d'exploitation avant et après chaque pile. Après chaque copie le système d'exploitation vérifie ces octets contre sa copie et se termine le programme si elles diffèrent. Cela résout beaucoup de problèmes de sécurité, mais il est toujours possible de copier des octets dans la pile pour remplacer le pointeur à la fonction pour gérer ce qui se passe lorsque ces octets qui ont été modifiés ainsi, vous permettant de faire la même chose. Il devient beaucoup plus difficile à faire.
En C il n'y a pas de vérification des limites de tableaux, de ses un compromis afin d'avoir de meilleures performances au risque de se tirer une balle dans le pied.
strcpy()
ne se soucie pas de savoir si la cible de la mémoire tampon est assez grande pour la copie trop d'octets va provoquer un comportement indéfini.c'est une des raisons pour lesquelles une nouvelle version de la fonction strcpy ont été introduits dans laquelle vous pouvez spécifier la cible de la taille de la mémoire tampon
strcpy_s()
Noter que sizeof(s) est déterminé au moment de l'exécution. Utiliser strlen() pour trouver le nombre de caractères s occupé. Lorsque vous effectuez la fonction strcpy() source chaîne sera remplacée par la chaîne de destination afin que votre sortie ne sera pas "Helloworld isnsadsdas"
Vous êtes en s'appuyant sur un comportement indéterminé tant que le compilateur a choisi de placer les deux tableaux où votre code arrive à travailler. Cela peut ne pas fonctionner dans l'avenir.
À la
sizeof
opérateur, c'est compris au moment de la compilation.Une fois que vous utiliser une matrice de tailles, vous devez utiliser
strlen
pour aller chercher de la longueur des chaînes.La meilleure façon de comprendre comment la fonction strcpy fonctionne en arrière-scène, c'est...la lecture de son code source!
Vous pouvez lire les sources pour la GLibC : http://fossies.org/dox/glibc-2.17/strcpy_8c_source.html . J'espère que cela aide!
Meilleure Solution est
{}
de mettre votre codeÀ la fin de chaque chaîne de caractères ou un tableau de chaînes de caractères il y a un
null terminator character '\0'
qui marque la fin de la chaîne de caractères ou un tableau de caractères.strcpy()
préformes sa tâche jusqu'à ce qu'il voit le '\0' caractère.printf()
également des préformes de sa tâche jusqu'à ce qu'il voit le '\0' caractère.sizeof()
d'autre part n'est pas intéressés par le contenu du tableau, seule sa taille allouée (comment grand il est censé l'être), donc pas de prendre en considération, lorsque la chaîne de caractères ou un tableau de chaînes de caractères se termine effectivement (comment grand il ne l'est réellement).Par opposition à sizeof(), il est
strlen()
que est intéressés dans combien de temps la chaîne est en fait (pas combien de temps il était censé être) et donc de compter le nombre de caractères jusqu'à ce qu'il atteigne la fin ('\0' caractères) où il s'arrête (il ne comprend pas le '\0' caractères).