2
votes

Comment ce code boucle sans aucune déclaration de boucle ou «goto» ou récursivité?

Le code suivant ne contient aucune boucle, ni goto , ni récursivité; pourtant il imprime de 1 à 10 dans la console.

#include <stdio.h>

int n = 1;

void foo() {
    int x;

    printf("%d ", n);
    if (++n>10) return;

    *(&x+4) -= 5;
}

int main() {
    foo();
    return 0;
}

Ce code mystérieux * (& x + 4) - = 5; est à l'origine de la boucle. p >


Autant que j'ai compris - la valeur de x est conservée dans la mémoire de la pile. Ainsi peut-être, avant que (& x + 4) il y a le pointeur de la fonction foo , et foo est appelé récursivement. p>

Là encore, je ne suis pas sûr que mes hypothèses soient exactes. Je ne comprends pas non plus d'où vient ce 5 . J'ai essayé d'imprimer et d'analyser les adresses (conseillées par mon collègue) du pointeur de fonction et des variables; et associez-les à ma connaissance de la disposition de la mémoire C . Mais je suis devenu plus confus.


S'il y avait plus de variables déclarées avant et après x , comment * (& x + 4) - = 5; code> changerait?


OS: Windows-7 64 bits, Compilateur: GNU GCC, Editeur: strong > CodeBlocks 16.01


10 commentaires

Il imprime uniquement 1 puis quitte.


Cela dépend d'un comportement non défini. Vous n'êtes pas censé déréférencer une mémoire à (& x + 4) lorsque x est simplement int x .


@Deanie J'ai utilisé le compilateur gcc. Imprime 1 à 10 en C et C ++.


@TheDeepThinker J'ai utilisé le compilateur gcc. Imprime 1 avec un espace, puis sort.


@SouravGhosh J'ai trouvé le code en ligne. Je sais, je ne devrais pas coder comme ça. Je veux simplement connaître l ' explication .


Code auto-modifiable reposant sur la disposition de la pile et les détails de l'architecture matérielle. Cette question est inutile sans ces détails fournis. Par ex. sur mon Fedora 29 x86_64 avec gcc 8.2.1 je n'obtiens qu'un défaut de segmentation. Pas de surprise là-bas, pour être honnête ...


@StefanBecker Ce n'est pas du "code auto-modifiable", c'est juste corrompre sa propre pile.


@duskwuff Merci pour votre commentaire. Pourriez-vous s'il vous plaît dire comment la pile est corrompue.


@duskwuff dans mon livre modifier l'adresse de retour sur la pile est un code auto-modifiable.


@StefanBecker Dans mon livre, ce n'est que du "code auto-modificateur" s'il s'agit de code modificateur . La pile est constituée de données, pas de code.


3 Réponses :


3
votes

Le comportement de * (& x + 4) - = 5; n'est pas défini car il écrit en dehors des limites de tout objet alloué par le programme. Ce que cela fera dépendra de ce qui sera stocké à cette adresse, le cas échéant. Donc, la réponse courte à la raison pour laquelle votre code se comporte si étrangement est que le code a un bogue qui rend son comportement imprévisible.

Ce qui se passe probablement sur votre plate-forme, c'est qu'elle finit probablement par modifier l'adresse à laquelle elle revient, la faisant revenir à main avant l'appel à foo résultant en main appelant à nouveau foo .


2 commentaires

Ce n'est pas parce que le code n'est pas valide en C ou C ++ qu'il est nécessairement bogué. S'il est livré avec des instructions qu'il est destiné à être compilé avec une version spécifique de GCC pour Windows 7, cela dépend du comportement du compilateur. Ce n'est peut-être même pas un comportement non documenté.


Je n'ai jamais vu de document de compilateur quelque chose comme ça. S'il repose sur un comportement non documenté, c'est bogué. Il n'y a aucun moyen de savoir dans quelles conditions le comportement se produira ou ne se produira pas.



2
votes

David a raison. Lorsqu'un code essaie d'écrire ou même d'accéder à quelque chose dans le bloc mémoire qui ne lui est pas alloué, il peut faire n'importe quoi (même une explosion nucléaire s'il y a, disons, l'adresse d'une fonction qui fait chier avec le réacteur). Il est de votre responsabilité de prendre en charge les allocations de mémoire car C n'est pas un langage fortement typé. C'est juste une co-incidence qui provoque une boucle. Sur mon système, cela fonctionne comme prévu.


0 commentaires

1
votes

Vous modifiez la pile dans * (& x + 4) - = 5; C'est un comportement indéfini, et sous un comportement non défini, tout est permis, même pour faire voler des démons hors de votre nez . Voir http://catb.org/jargon/html/N/nasal-demons .html

Peut-être que pour un HW spécifique avec un compilateur spécifique, vous obtenez toujours le même comportement, mais ce n'est pas nécessaire.


0 commentaires