Oui, ça relève de la magie noire ^^
Y'a des tas de conventions d'appel différentes, mais prenons le cas des plus courantes, que tu as certainement déjà rencontré
Sur nos chère TI 68k et sur x86, et normalement sur toutes les plates-formes en supposant qu'il y ait pas d'histoires de registres dans ta fonction, le comportement normal de la convention d'appel C (cdecl) est le suivant:
On empile les arguments, en partant du dernier et en respectant l'alignement des données (déjà un premier truc non portable ^^), puis on appelle la fonction (oui ça peut empiler je ne sais quels truc mais c'est pas ton job de savoir quoi exactement, ça dépend encore plus de la plate-forme cible).
Au retour de la fonction tu reçois la valeur de retour dans le(s) "premier(s) registre(s)" adapté(s) si ça tient dedans et sinon heu c'est nettement plus compliqué, mais c'est souvent un truc du genre tu alloues la mémoire avant l'appel (normal quoi) et tu files le pointeur à la fonction appelée en tant que premier (= empilé en dernier) paramètre. Et la fonction se fera (peut-être) une joie de te retourner ce pointeur comme valeur de retour (ou pas). J'avoue que ce point est plutôt flou
Une fois que t'es content avec tout ça, tu commences par dépiler tous les trucs qui viennent polluer la pile, et tu continues gaiement ton exécution.
Si tu utilises stdcall (sous Win32) par exemple, c'est comme cdecl, sauf que c'est la fonction apellée (donc pas toi) qui nettoie la pile. Au moindre pet de travers, tu as foutu la pile, et donc toute l'éxécution du programme, en l'air... (Inutile de préciser que du coup ça ne gère pas les arguments variables)
En gros pour appeler une fonction, tu as besoin, de la convention d'appel utilisée (dépendant de l'architecture + plate-forme + éventuellement réglage du compilateur), de son prototype, du prototype des types utilisés dans le prototype (et de même jusqu'à ce que "types utilisés dans [...]" = 0)... Et tout ça dépend joyeusement de l'architecture. C'est pour ça que tu te simplifies bien la vie à utiliser un truc portable (c'est à dire où on te file une interface standard sur un truc non portable) qui fait ça pour toi, et quand tu peux pas, à définir des convention d'appel plus arrangeantes dans ton programme (par exemple, comme dit sally, utiliser des union)