2009-11-06 11 views
4

Lors d'un appel de fonction sous Linux (ou OS X d'ailleurs), l'appelé peut-il modifier les valeurs des arguments de la pile? J'étais sous l'hypothèse que puisque l'appelant est celui qui les nettoie, qu'ils devraient contenir les mêmes valeurs après l'appel de la fonction. Cependant, j'ai trouvé que GCC avec -O2 modifiait les paramètres qui lui étaient passés sur la pile. J'ai également cherché de la documentation, y compris les conventions d'appel System V i386, mais n'a pas pu trouver une réponse définitive à cela.C conventions d'appel et arguments passés

Voici un exemple de code que j'ai débogué.

pushl %eax  # %eax = 0x28 
call _print_any 
popl %eax 
       # %eax is now 0x0a 

Je suppose que GCC modifier ce paramètre sur la pile est très bien, mais je veux savoir où il est précisé qu'il peut faire.

+0

"modifier" de quelle façon? Vous vous rendez compte que si vous ne dites pas au compilateur de ne pas le faire, il n'y a aucune raison de s'attendre à ce qu'il ne muck pas avec les valeurs sur la pile afin de compléter ses optimisations? –

+0

Le comportement du code C et le fonctionnement des conventions d'appel peuvent être très différents. Voici les conventions d'appel du System V C pour i386 si vous êtes curieux. Il a tendance à être quelque peu vague sur certaines choses, cependant ... http://www.sco.com/developers/devspecs/abi386-4.pdf –

Répondre

3

Bien que l'appelant (dans certaines conventions d'appel) est celui qui nettoie les arguments, tout cela fait vraiment est désaffecter l'espace précédemment alloué sur la pile pour maintenir les valeurs de l'argument. L'appelé est libre de modifier les valeurs lors de l'exécution de la fonction, car l'appelant ne va pas regarder leurs valeurs plus tard.

Dans l'exemple que vous avez publié, GCC a émis l'instruction popl %eax pour libérer l'espace pris par le paramètre sur la pile. Tout ce qu'il faut vraiment faire est d'ajouter 4 à% esp (la pile sur x86 pousse vers le bas en mémoire), et l'exécution de l'instruction popl %eax est le moyen le plus court et le plus rapide pour ce faire. Si le compilateur devait libérer 20 valeurs, il modifierait probablement% esp directement au lieu d'émettre 20 instructions popl.

Vous trouverez probablement que la nouvelle valeur de% eax n'est pas utilisée dans le code suivant.

+0

En fait, c'était à partir du compilateur sur lequel je travaillais, mais je cachais une valeur qui est en direct plus tard dans le programme. J'ai supposé que c'était sûr et que je voulais éviter de mettre deux fois sa valeur sur la pile (une fois pour enregistrer et une fois pour le passage des arguments). –

+0

Dans ce cas, vous ne pouvez certainement pas * supposer que la valeur éclatée sera toujours égale à ce que vous avez précédemment poussé. L'appelé est libre de modifier ses arguments sur la pile (ils sont traités de la même façon que les variables locales). –

1

Oui, l'appelé peut modifier les arguments de la pile. En ce qui concerne l'appelé, ils sont les mêmes que les variables locales. L'appelant les nettoie mais ignore la valeur.

Si vous parlez POD C ou C++, le nettoyage consiste simplement à modifier le pointeur de la pile. Si vous parlez de C++ avec un destructeur, l'appelant est responsable de l'appel du destructeur, mais les destructeurs de classes génériques doivent être écrits pour nettoyer toute valeur.

1

En C standard, l'appelé peut modifier les valeurs de ses arguments tout ce qu'il veut, mais l'appelant ne verra jamais les changements. Ce qui peut prêter à confusion est que si l'on passe un POINTER à une valeur, l'appelé peut changer cette valeur en déréférençant le pointeur, mais si l'appelé change réellement le pointeur lui-même, l'appelant ne verra pas ce changement.

Un petit zéro: le standard C ne nécessite pas que l'implémentation ait même une pile.

+0

Je ne pense pas que cela répond aux préoccupations de OP. La question initiale ne concerne pas tellement le langage C, mais une implémentation spécifique (comme dans, si je ne le change pas, pourquoi le compilateur le fait-il?). Je ne vais pas rétrograder, mais je pense que vous vous dirigez dans la mauvaise direction. –

+0

Oui, en pratique, un argument pourrait ne jamais être dans une pile, il serait dans un registre nommé (ceci est particulièrement vrai dans les plates-formes avec beaucoup de registres - comme MIPS). – joemoe

+0

Je ne parle pas des arguments en C, je parle d'appeler des fonctions C depuis l'assemblage et au niveau de la machine. –

0

Si vous passez par la valeur:

call do_it(to_it); 

L'argument est copié (probablement au début de la pile, mais peut-être pas en fonction de votre compilateur) le programme unicellulaire peut jouer avec cette copie autant qu'il veut mais la variable dans le programme de clling ne sera pas modifiée.

Si vous passez par référence:

call do_it(&to_it); 

ensuite l'adresse de la variable est passée. Toute modification apportée par la variable appelée sera à la variable d'origine dans le programme appelant.

+0

Je ne parle pas dans un langage comme C ou C++ (ni passant par référence/valeur). Je parle des paramètres actuels de la pile d'exécution au niveau de la machine et de savoir si ces emplacements de pile peuvent être modifiés ou non. –