В C++ сделайте функции variadic (те, которые имеют … в конце списка параметров), обязательно следуют __ cdecl соглашение о вызовах?

Я знаю, что __ stdcall функции не может иметь замещающих знаков, но я хочу быть уверенным, что нет никаких платформ, которые поддерживают функции stdarg.h для соглашений о вызовах кроме __ cdecl или __ stdcall.

7
задан Ben 25 March 2010 в 02:22
поделиться

4 ответа

Соглашение о вызове должно быть таким, чтобы вызывающая сторона очищала аргументы из стека (потому что вызывающая сторона не знает, что будет передано).

Однако это не обязательно соответствует тому, что Microsoft называет "__cdecl". Например, на SPARC аргументы обычно передаются в регистрах, потому что так устроен SPARC - его регистры в основном действуют как стек вызовов, который высыпается в основную память, если вызовы становятся настолько глубокими, что уже не помещаются в регистр.

Хотя я менее уверен в этом, я бы ожидал примерно того же на IA64 (Itanium) - у него тоже огромный набор регистров (несколько сотен, если мне не изменяет память). Если я не ошибаюсь, он немного более свободен в использовании регистров, но я бы ожидал, что они будут использоваться аналогично, по крайней мере, большую часть времени.

Почему это имеет для вас значение? Смысл использования stdarg.h и его макросов в том, чтобы скрыть от вашего кода различия в соглашениях о вызовах, чтобы он мог работать с переменными аргументами переносимо.

Редактирование, на основе комментариев: Хорошо, теперь я понимаю, что вы делаете (по крайней мере, достаточно, чтобы улучшить ответ). Учитывая, что у вас уже (очевидно) есть код для обработки вариаций в ABI по умолчанию, все стало проще. Остается только вопрос о том, всегда ли переменные функции используют "стандартный ABI", каким бы он ни был для данной платформы. С "stdcall" и "default" в качестве единственных опций, я думаю, что ответ на этот вопрос - да. Например, в Windows, wsprintf и wprintf нарушают это правило и используют соглашение о вызове cdecl вместо stdcall.

8
ответ дан 6 December 2019 в 23:04
поделиться

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

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

stdcall не будет работать, потому что вызываемый объект отвечает за извлечение параметров из стека. В старые времена 16-битной Windows pascal не работал, потому что он помещал параметры в стек слева направо.

Конечно, как упоминалось в других ответах, многие платформы не дают вам никакого выбора с точки зрения соглашения о вызовах, что делает этот вопрос неактуальным для тех.

2
ответ дан 6 December 2019 в 23:04
поделиться

Вы имеете в виду «платформы, поддерживаемые MSVC» или как общее правило? Даже если вы ограничитесь платформами, поддерживаемыми MSVC, у вас все равно будут ситуации, подобные IA64 и AMD64 где есть только «одно» соглашение о вызовах, и это соглашение о вызовах называется __ stdcall , но это определенно не то же самое __ stdcall , которое вы используете на x86.

0
ответ дан 6 December 2019 в 23:04
поделиться

AFAIK, разнообразие соглашений о вызовах уникально для DOS / Windows на x86. На большинстве других платформ компиляторы поставлялись с ОС и стандартизировали соглашения.

0
ответ дан 6 December 2019 в 23:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: