Comment les arguments de fonction sont-ils stockés en mémoire?

En essayant de créer ma propre alternative aux macros stdarg.h pour les fonctions à arguments variables, fonctions avec un nombre inconnu d’arguments, j’ai essayé de comprendre la façon dont les arguments sont stockés en mémoire.

Voici un MWE:

#include  void foo(int num, int bar1, int bar2) { printf("%p %p %p %p\n", &foo, &num, &bar1, &bar2); } int main () { int i, j; i = 3; j = -5; foo(2, i, j); return 0; } 

Je comprends sans problème que l’adresse de la fonction n’est pas au même endroit que les adresses des arguments. Mais ces derniers ne sont pas toujours organisés de la même manière.

Sur une architecture x86_32 (mingw32), j’obtiens ce genre de résultat:

 004013B0 0028FEF0 0028FEF4 0028FEF8 

ce qui signifie que les adresses sont dans le même ordre que les arguments.

MAIS quand je le lance sur un x86_64 cette fois la sortie est:

 0x400536 0x7fff53b5f03c 0x7fff53b5f038 0x7fff53b5f034 

Où les adresses sont évidemment dans l’ordre inverse des arguments.

Donc ma question est (tl; dr):

L’architecture des adresses des arguments est-elle dépendante ou également du compilateur?

Il dépend du compilateur. Les fabricants de compilateurs doivent naturellement obéir aux règles de l’architecture de la CPU. Normalement, un compilateur obéit également à la plate-forme ABI, du moins pour le code qui pourrait potentiellement interagir avec le code produit par un autre compilateur. La plateforme ABI est une spécification de la convention d’appel, reliant la sémantique et bien plus encore, pour une plateforme donnée.

Par exemple, les compilateurs sous Linux et autres systèmes d’exploitation similaires à Unix adhèrent à l’ interface binary d’application System V et vous trouverez au chapitre 3.2.3 comment les parameters sont transmis aux fonctions (les arguments passés dans les registres sont passés de gauche à droite et ceux passés en mémoire) (sur la stack) sont passés de droite à gauche). Sous Windows, les règles sont documentées ici .

Ils sont dépendants d’ ABI . Dans les cas où cela n’a pas d’importance (les fonctions ne seront appelées que de manière connue), cela dépend entièrement du compilateur, ce qui implique généralement l’utilisation de registres, qui n’ont pas d’adresse (ces arguments auront une adresse si vous le demandez). cette adresse, donnant l’apparence que tout a une adresse). Les fonctions qui sont en ligne n’ont même plus vraiment d’arguments, alors la question de savoir quelles sont leurs adresses est sans object – bien qu’une fois encore, elles sembleront exister et auront une adresse lorsque vous le forcerez.

Les arguments ne peuvent pas du tout être stockés en mémoire, mais passés via des registres; cependant, la langue nécessite le renvoi d’une adresse pour tout opérande de symbole de & , votre observation peut donc résulter d’une tentative d’observation effective et le compilateur a simplement copié les valeurs dans ces adresses afin qu’elles soient adressables.

Il peut être intéressant de voir ce qui se passe si vous demandez les adresses dans un ordre différent de leur transmission, par exemple:

 printf("%p %p %p %p\n", &num, &bar1, &bar2, &foo) ; 

Vous pouvez ou non obtenir le même résultat; le fait est que les adresses que vous avez observées peuvent être un artefact de l’observation plutôt que du passage. Certes, dans l’ABI ARM, les quatre premiers arguments d’une fonction sont passés dans les registres R0, R1, R2 et R3, puis par la stack.

Sur x86_64, vous obtenez les arguments dans un ordre “étrange” car ils ne sont réellement transmis à la fonction dans aucune mémoire. Ils sont passés dans les registres du processeur. En prenant leur adresse, vous obligez en réalité le compilateur à générer du code qui stockera les arguments en mémoire (sur la stack dans votre cas) afin que vous puissiez en prendre l’adresse.

Vous ne pouvez pas implémenter les macros stdarg sans interagir avec le compilateur. Dans gcc, les macros stdarg encapsulent simplement une construction intégrée, car il n’ya aucun moyen de savoir où se trouvent les arguments au moment où vous en avez besoin (le compilateur a peut-être réutilisé les registres). Le support stdarg intégré à gcc peut considérablement modifier la génération de code pour les fonctions qui les utilisent, de sorte que les arguments sont disponibles. Je présume qu’il en va de même pour les autres compilateurs.