Может ли stdcall иметь переменные аргументы?

Насколько мне известно, переменные аргументы можно использовать только в соглашении об очистке стека вызывающей стороны.
Кстати, WinApi StringCchPrintfW объявляется так (я удалил SAL).

__inline HRESULT __stdcall
StringCchPrintfW(
STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszFormat, ...
);

Может ли stdcall также иметь переменные аргументы?


person Benjamin    schedule 01.09.2010    source источник


Ответы (1)


Нет. Соглашение о вызовах stdcall имеет вызываемый объект очищает стек. Поскольку вызываемый объект очищает стек, у него нет возможности узнать во время компиляции, сколько нужно удалить, поэтому он не может иметь переменные аргументы.

Чтобы иметь переменное количество аргументов функции, вам нужно использовать cdecl, при котором вызывающая сторона очищает стек. Это все компилятор, чтобы определить, сколько аргументов передается, и поскольку вызывающая сторона очищает стек, он также знает, сколько нужно вытолкнуть из стека, когда вызов функции возвращается.

В случае, упомянутом выше, функция объявляется для использования __stdcall, которая, как упоминалось ранее, не поддерживает переменные аргументы. В этом случае компилятор принимает решение игнорировать определенное соглашение о вызовах и вернуться к __cdecl. Это поведение упоминается в описании stdcall, упомянутое выше. Я цитирую:

Вызываемый объект очищает стек, поэтому компилятор делает функции vararg __cdecl.

Это можно наблюдать, если скомпилировать следующий код и дизассемблировать вызов функции.

int __stdcall Bar(int a, int b, ...)
{
  return b * a;
}

Полученный код будет обработан как __cdecl. Что касается причины, что это определено именно так, я не знаю.

person linuxuser27    schedule 01.09.2010
comment
Да, извините за это. Запутался со старым соглашением о вызовах pascal. - person linuxuser27; 01.09.2010
comment
Кстати, я не думаю, что дело в порядке слева направо или справа налево. Дело в том, кто несет ответственность за очистку стека. - person Benjamin; 01.09.2010
comment
В яблочко. Я только что обновил свой ответ. До stdcall существовало соглашение о вызовах pascal, вы можете найти его в ссылке, на которую вы ссылаетесь. У этого был аргумент слева направо. - person linuxuser27; 01.09.2010
comment
Это (встроено) имеет смысл. Но почему они написали stdcall явно. Это приводит меня в замешательство. - person Benjamin; 01.09.2010
comment
Ваш ответ был изменен на хороший! Спасибо. - person Benjamin; 01.09.2010
comment
@Benjamin: Справа налево против слева направо также играет роль. В R2L первый аргумент находится на вершине стека, поэтому вы можете использовать его, чтобы выяснить, сколько еще аргументов находится в стеке. В L2R аргумент последний находится в конце стека. Поскольку это переменный аргумент неизвестного типа, нет особого смысла выяснять, что еще было передано. - person Jerry Coffin; 01.09.2010
comment
@Jerry Coffin: Что означает какой-то неизвестный тип? Дайте мне больше подсказок. Спасибо. - person Benjamin; 18.09.2010
comment
@Benjamin: Рассмотрим в качестве примера printf. Первый аргумент сообщает типы остальных аргументов. С аргументами, переданными R2L, он находится на вершине стека, поэтому printf может использовать его для определения типов других. Если аргументы передают L2R, то TOS будет последним переданным аргументом, который может быть int, long, double или чем-то еще, но ничего не говорит printf о других аргументах. - person Jerry Coffin; 20.09.2010