В CDECL аргументы помещаются в стек в обратном порядке, вызывающая сторона очищает стек, а результат возвращается через реестр процессора (позже я буду называть его «регистр A»). В STDCALL есть одно отличие: вызывающий не очищает стек, а вызывающий.
Вы спрашиваете, какой из них быстрее. Ни один. Никто. Вы должны использовать нативное соглашение о вызовах столько, сколько сможете. Изменяйте соглашение только в том случае, если нет выхода, при использовании внешних библиотек, которые требуют использования определенного соглашения.
Кроме того, есть и другие соглашения, которые компилятор может выбрать по умолчанию, например, компилятор Visual C++ использует FASTCALL, который теоретически быстрее из-за более широкого использования регистров процессора.
Обычно вы должны дать правильную подпись соглашения о вызовах для функций обратного вызова, переданных в некоторую внешнюю библиотеку, т.е. обратный вызов qsort
из библиотеки C должен быть CDECL (если компилятор по умолчанию использует другое соглашение, тогда мы должны пометить обратный вызов как CDECL) или различные обратные вызовы WinAPI должны быть быть STDCALL (весь WinAPI является STDCALL).
Другой обычный случай может быть, когда вы храните указатели на некоторые внешние функции, т.е. для создания указателя на функцию WinAPI ее определение типа должно быть помечено STDCALL.
А ниже пример, показывающий, как это делает компилятор:
/* 1. calling function in C++ */
i = Function(x, y, z);
/* 2. function body in C++ */
int Function(int a, int b, int c) { return a + b + c; }
CDECL:
/* 1. calling CDECL 'Function' in pseudo-assembler (similar to what the compiler outputs) */
push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x'
call (jump to function body, after function is finished it will jump back here, the address where to jump back is in registers)
move contents of register A to 'i' variable
pop all from the stack that we have pushed (copy of x, y and z)
/* 2. CDECL 'Function' body in pseudo-assembler */
/* Now copies of 'a', 'b' and 'c' variables are pushed onto the stack */
copy 'a' (from stack) to register A
copy 'b' (from stack) to register B
add A and B, store result in A
copy 'c' (from stack) to register B
add A and B, store result in A
jump back to caller code (a, b and c still on the stack, the result is in register A)
СТАНДАРТНЫЙ ВЫЗОВ:
/* 1. calling STDCALL in pseudo-assembler (similar to what the compiler outputs) */
push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x'
call
move contents of register A to 'i' variable
/* 2. STDCALL 'Function' body in pseaudo-assembler */
pop 'a' from stack to register A
pop 'b' from stack to register B
add A and B, store result in A
pop 'c' from stack to register B
add A and B, store result in A
jump back to caller code (a, b and c are no more on the stack, result in register A)
person
adf88
schedule
04.08.2010