Tout programmeur C qui travaille depuis plus d'une semaine a rencontré des plantages résultant de l'appel de printf
avec plus de spécificateurs de format que d'arguments réels, par exemple:
printf("Gonna %s and %s, %s!", "crash", "burn");
Cependant, y a-t-il des mauvaises choses similaires qui peuvent se produire lorsque vous passez trop d'arguments à printf?
printf("Gonna %s and %s!", "crash", "burn", "dude");
Ma connaissance de l'assemblage x86/x64 me porte à croire que cela est inoffensif, bien que je ne sois pas convaincu qu'il n'y ait pas de condition Edge qui me manque, et je n'ai aucune idée des autres architectures. Cette condition est-elle garantie inoffensive, ou y a-t-il un écueil potentiellement accidentel ici aussi?
Vous connaissez probablement le prototype de la fonction printf comme quelque chose comme ça
int printf(const char *format, ...);
Une version plus complète serait en fait
int __cdecl printf(const char *format, ...);
Le __cdecl
définit la "convention d'appel" qui, avec d'autres choses, décrit comment les arguments sont traités. Dans ce cas, cela signifie que les arguments sont poussés sur la pile et que la pile est nettoyée par la fonction effectuant l'appel.
Une alternative à _cdecl
est __stdcall
, il y en a d'autres. Avec __stdcall
la convention est que les arguments sont poussés sur la pile et nettoyés par la fonction qui est appelée. Cependant, pour autant que je sache, il n'est pas possible pour un __stdcall
fonction pour accepter un nombre variable d'arguments. Cela a du sens car il ne saurait pas combien de pile à nettoyer.
Le long et le court, c'est que dans le cas de __cdecl
fonctionne de manière sécurisée pour passer autant d'arguments que vous le souhaitez, car le nettoyage est effectué dans le code faisant l'appel. Si vous deviez en quelque sorte passer trop d'arguments à un __stdcall
fonction cela entraîne une corruption de la pile. Un exemple où cela pourrait se produire est si vous aviez le mauvais prototype.
Plus d'informations sur les conventions d'appel peuvent être trouvées sur Wikipedia ici .
Projet de norme C en ligne (n1256) , section 7.19.6.1, paragraphe 2:
La fonction fprintf écrit la sortie dans le flux pointé par stream, sous le contrôle de la chaîne pointée par format qui spécifie comment les arguments suivants sont convertis pour la sortie. S'il n'y a pas suffisamment d'arguments pour le format, le comportement n'est pas défini. Si le format est épuisé alors que les arguments restent, les arguments en excès sont évalués (comme toujours) mais sont sinon ignorés. La fonction fprintf renvoie lorsque la fin de la chaîne de format est rencontrée.
Le comportement de toutes les autres fonctions *printf()
est le même que les arguments excédentaires sauf pour vprintf()
(évidemment).
Tous les arguments seront poussés sur la pile et supprimés si le cadre de pile est supprimé. ce comportement est indépendant d'un processeur spécifique. (Je me souviens seulement d'un mainframe qui n'avait pas de pile, conçu dans les années 70) Donc, oui, le deuxième exemple n'échouera pas.
printf
est conçu pour accepter n'importe quel nombre d'arguments. printf lit ensuite le spécificateur de format (premier argument) et extrait les arguments de la liste des arguments selon les besoins. C'est pourquoi trop peu d'arguments se bloquent: le code commence simplement à utiliser des arguments inexistants, à accéder à de la mémoire qui n'existe pas ou à quelque autre mauvaise chose. Mais avec trop d'arguments, les arguments supplémentaires seront simplement ignorés. Le spécificateur de format utilisera moins d'arguments que ce qui a été transmis.