Le C et le C++ standard n'ont aucune exigence sur la façon dont il doit fonctionner. Un respect compilateur peut décider d'émettre des listes chaînées, std::stack<boost::any>
ou même magique poney poussière (comme par Xeo) sous le capot.
Toutefois, il est généralement mis en œuvre comme suit, même si les transformations comme d'inlining ou de passer des arguments dans les registres du CPU ne peut pas laisser quoi que ce soit de l'examen du code.
Veuillez également noter que cette réponse décrit expressément à la baisse croissante de la pile dans les visuels ci-dessous; aussi, cette réponse est une simplification, simplement pour démontrer le système (veuillez voir https://en.wikipedia.org/wiki/Stack_frame).
Comment une fonction est appelée avec un non-nombre fixe d'arguments
Cela est possible parce que le sous-jacent de l'architecture de la machine a une sorte de "pile" pour chaque thread. La pile est utilisé pour transmettre des arguments de fonctions. Par exemple, lorsque vous avez:
foobar("%d%d%d", 3,2,1);
Alors cette compile un code assembleur comme ceci (exemplaire et schématiquement, code réel peut être légèrement différent); notez que les arguments sont passés de droite à gauche:
push 1
push 2
push 3
push "%d%d%d"
call foobar
Ces push-opérations de remplissage de la pile:
[] // empty stack
-------------------------------
push 1: [1]
-------------------------------
push 2: [1]
[2]
-------------------------------
push 3: [1]
[2]
[3] // there is now 1, 2, 3 in the stack
-------------------------------
push "%d%d%d":[1]
[2]
[3]
["%d%d%d"]
-------------------------------
call foobar ... // foobar uses the same stack!
Le bas de la pile élément est appelé le "Haut de la Pile", souvent abrégé en "TOS".
L' foobar
fonction serait désormais accéder à la pile, en commençant à la TOS, c'est à dire la chaîne de format, qui, comme vous vous en souvenez, a été poussé dernier. Imaginez stack
est votre pointeur de pile , stack[0]
est la valeur du TOS, stack[1]
est l'un au-dessus de la CDU, et ainsi de suite:
format_string <- stack[0]
... puis analyse la chaîne de formatage. Lors de l'analyse, il recognozies l' %d
-jetons, et, pour chacun, les charge plus de valeur à partir de la pile:
format_string <- stack[0]
offset <- 1
while (parsing):
token = tokenize_one_more(format_string)
if (needs_integer (token)):
value <- stack[offset]
offset = offset + 1
...
Bien sûr, cela est très incomplète pseudo-code qui montre comment la fonction est de s'appuyer sur les arguments passés à savoir combien il a à charge et retirer de la pile.
Sécurité
Cette dépendance à l'égard de l'utilisateur fourni des arguments est également l'un des plus grands problèmes de sécurité (voir la section https://cwe.mitre.org/top25/). Les utilisateurs peuvent facilement utiliser un variadic fonction à tort, soit parce qu'ils n'ont pas lu la documentation, ou oublié de régler le format de la chaîne ou de la liste d'arguments, ou parce qu'ils sont mal plaine, ou quoi que ce soit. Voir également la Chaîne de Format d'Attaque.
C Mise En Œuvre
En C et C++, variadic fonctions sont utilisés avec l' va_list
interface. Tout en le poussant sur la pile est intrinsèque à ces langues (en K+R C vous pouvez même l'avant-déclarer une fonction sans en exposant ses arguments, mais encore l'appeler par n'importe quel nombre et le type des arguments), la lecture d'un tel argument inconnu liste est connectée par l' va_...
-macros et va_list
-type, qui, fondamentalement, les résumés du faible niveau de pile-cadre de l'accès.