Как функции vararg узнают количество аргументов в машинном коде? - PullRequest
9 голосов
/ 11 марта 2011

Как различные функции, такие как printf , могут узнать количество полученных аргументов?

Количество аргументов явно не передается в качестве (скрытого) параметра (см. вызов printf в примере asm здесь ).

Что за хитрость?

Ответы [ 4 ]

10 голосов
/ 11 марта 2011

Хитрость в том, что вы говорите им как-то еще.Для printf вы должны указать строку формата, которая даже содержит информацию о типе (хотя это может быть неверно).Способ предоставления этой информации в основном заключается в пользовательском контракте и часто подвержен ошибкам.

Что касается соглашений о вызовах: обычно аргументы помещаются в стек слева направо, а затем, наконец, адрес обратного перехода.Вызывающая процедура очищает стек.Таким образом, нет необходимости в том, чтобы вызываемая подпрограмма знала количество параметров.

РЕДАКТИРОВАТЬ: В C ++ 0x есть безопасный (даже небезопасный!) Способ вызывать функции с переменными числами!

9 голосов
/ 11 марта 2011

Неявно, из строки формата.Обратите внимание, что stdarg.h не содержит макросов для получения общего «переменного» числа переданных аргументов.Это также одна из причин, по которой соглашение о вызовах C требует, чтобы вызывающая сторона очищала стек, даже если это увеличивает размер кода.

7 голосов
/ 14 марта 2011

Это причина, по которой аргументы выдвигаются в обратном порядке в соглашении о вызовах C, например:

Если вы позвоните:

printf("%s %s", foo, bar);

Стек заканчивается как:

  ...
+-------------------+
| bar               |
+-------------------+
| foo               |
+-------------------+
| "%s %s"           |
+-------------------+
| return address    |
+-------------------+
| old frame pointer | <- frame pointer
+-------------------+
  ...

Аргументы принимаются косвенно, используя его смещение от указателя кадра (указатель кадра может быть опущен умными компиляторами, которые знают, как вычислить вещи из указателя стека). Первый аргумент всегда находится по известному адресу в этой схеме, функция получает столько аргументов, сколько ей говорят ее первые аргументы.

Попробуйте следующее:

printf("%x %x %x %x %x %x\n");

Это сбросит часть стека.

3 голосов
  • AMD64 System V ABI (Linux, Mac OS X) передает переменные вектора чисел (SEE / AVX) в rax, в отличие от IA-32.См. Также: Почему% eax обнуляется перед вызовом printf?

    TODO, почему это требуется?Я думаю, что это только из соображений производительности, чтобы избежать сохранения ненужных регистров SSE в «области сохранения регистров», упомянутой в «3.5.7 Списках аргументов переменных».

  • На уровне C,Есть также другие методы, кроме синтаксического анализа строки формата, как упоминалось другими.Вы также можете:

    • передать стражу (void *)0, чтобы указать последний аргумент, как execl делает.

      Вы захотите использовать sentinel атрибут функции, помогающий GCC принудительно применить это во время компиляции: C предупреждение Отсутствие дозорного при вызове функции

    • передать его в качестве дополнительного целочисленного аргумента с количеством varargs

    • используйте атрибут функции format, чтобы помочь GCC обеспечить принудительное форматирование строк известных типов, таких как printf или strftime

...