Стандартът C и C++ няма никакви изисквания за това как трябва да работи. Съответстващият компилатор може да реши да излъчи верижни списъци, std::stack<boost::any>
или дори магически прах от пони (според коментара на @Xeo) под капака.
Въпреки това, обикновено се изпълнява по следния начин, въпреки че трансформации като вграждане или предаване на аргументи в регистрите на процесора може да не оставят нищо от обсъждания код.
Моля, имайте предвид също, че този отговор конкретно описва нарастващ надолу стек във визуализациите по-долу; освен това този отговор е опростяване само за демонстриране на схемата (моля, вижте https://en.wikipedia.org/wiki/Stack_frame).
Как може да се извика функция с нефиксиран брой аргументи
Това е възможно, защото основната архитектура на машината има така наречения "стек" за всяка нишка. Стекът се използва за предаване на аргументи на функции. Например, когато имате:
foobar("%d%d%d", 3,2,1);
След това това се компилира до код на асемблер като този (примерно и схематично действителният код може да изглежда различно); обърнете внимание, че аргументите се предават отдясно наляво:
push 1
push 2
push 3
push "%d%d%d"
call foobar
Тези натискащи операции запълват стека:
[] // 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!
Най-долният стеков елемент се нарича "Върхът на стека", често съкратено "TOS".
Функцията foobar
сега ще има достъп до стека, започвайки от TOS, т.е. форматиращия низ, който, както си спомняте, беше избутан последен. Представете си, че stack
е вашият указател на стека, stack[0]
е стойността в TOS, stack[1]
е едно над TOS и така нататък:
format_string <- stack[0]
... и след това анализира форматния низ. Докато анализира, той разпознава %d
-токените и за всеки зарежда още една стойност от стека:
format_string <- stack[0]
offset <- 1
while (parsing):
token = tokenize_one_more(format_string)
if (needs_integer (token)):
value <- stack[offset]
offset = offset + 1
...
Това разбира се е много непълен псевдокод, който демонстрира как функцията трябва да разчита на предадените аргументи, за да разбере колко трябва да зареди и премахне от стека.
Сигурност
Това разчитане на аргументи, предоставени от потребителя, също е един от най-големите съществуващи проблеми със сигурността (вижте https://cwe.mitre.org/top25/). Потребителите могат лесно да използват variadic функция погрешно, или защото не са прочели документацията, или са забравили да коригират форматиращия низ или списъка с аргументи, или защото са просто зли, или каквото и да било. Вижте също Атака за форматиране на низ.
C Изпълнение
В C и C++ променливите функции се използват заедно с интерфейса va_list
. Въпреки че натискането в стека е присъщо на тези езици (в K+R C можете дори да декларирате функция напред, без да посочвате нейните аргументи, но все пак да я извикате с произволен брой и тип аргументи), четенето от такъв списък с неизвестни аргументи се свързва чрез va_...
-макроси и va_list
-тип, който основно абстрахира достъпа до стекова рамка на ниско ниво.
person
Sebastian Mach
schedule
16.04.2014
You just copy&pasted the answer. So, this question is duplicate of other.
Това е non sequitur. Дублиращи се отговори не създават дублиращи се въпроси. - person Lightness Races in Orbit   schedule 16.04.2014