Здравствуйте, Centaur, Вы писали:
C>Не то чтобы это было Microsoft-specific, это Windows-specific. В Win32 API большинство функций __stdcall, но изредка встречаются __cdecl (например, wsprintf), поэтому всякий Windows-программист должен представлять, что такое calling convention и чем ему грозит неправильное указание соглашения (как правило, срывом стека).
Это не Windows-specific, а x86-specific.
Разница между конвенциями cdecl (стек очищает вызывающая сторона) и stdcall (стек очищает вызываемая функция) связана с тем, что у i8086 есть две команды возврата — RET и RET n. Вторая экономит код (не нужно во всех местах расставлять ADD ESI,n). Однако язык Си, который поддерживает функции с переменным числом параметров, не может обойтись более компактным stdcall'ом.
Программисту нужно следить за типами, а не за конвенциями.
В тип функции входят
— тип возвращаемого значения
— конвенция вызова
— количество и типы аргументов
Несовпадение любого из этих пунктов приведёт к приключениям, особенно, если функция вызывается косвенно (например, callback).
Поэтому можно всего лишь формально объявлять функции так, как они прописаны в апишных хедерах — и не париться, скрывается ли за STDMETHODCALLTYPE конвенция __stdcall, или же __cdecl.
Здравствуйте, <Аноним>, Вы писали:
А>Здрасьте. Где бы мне почитать про все эти __cdecl, __declspec и т. д. Можно ссылку, плиз?
Согла- Порядок Очистка Расширение Примечания
шение передачи стека имени
вызова аргументов
__cdecl Справа Аргументы из К имени функции Применяется
налево стека удаляются добавляется пре- по умолчанию
вызывающей фикс из символа функциями
функцией. подчеркивания: C и C++
Только это сог- например, _Foo
лашение поддер-
живает перемен-
ное число
аргументов
функций.
__stdcall Справа Вызванная К имени функции Используется почти
налево функция сама добавляется пре- всеми системными
удаляет свои фикс из символа функциями;
аргументы из подчеркивания Используется по
стека. и суффикс из сим- умолчанию внут-
вола @, за которым ренними функциями
следует десятич- Visual Basic
ный размер списка
аргументов в бай-
тах: например,
_Foo@12
__fastcall Первые два Вызванная К имени функции Поддерживается
параметра функция сама добавляется пре- только процессорами
типа DWORD удаляет свои фикс из символа с архитектурой Intel.
передаются аргументы из @ и суффикс из Это соглашение ис-
в регистрах стека. этого же символа, пользуется по умолча-
ECX и EDX; за которым сле- нию компиляторами
Остальные дует десятичный Borland Delphi.
параметры размер списка
передаются аргументов в бай-
справа налево. тах: например,
@Foo@12
this Справа налево. Аргументы из Нет. Автоматически приме-
(__thiscall) Параметр стека удаляются няется к методам клас-
this вызывающей сов C++, пока вы не ука-
передается функцией. жете стандартное согла-
в регистре шение вызова. При
ECX. объявлении методов COM
используется стандартное
соглашение вызова.
naked Справа налево. Аргументы из Нет. Используется, когда
стека удаляются программисту нужно
вызывающей написать собственные
функцией. пролог и эпилог.
Здравствуйте, Warturtle, Вы писали:
W>Здравствуйте, Odi$$ey, Вы писали:
OE>>
OE>>Согла- Порядок Очистка Расширение Примечания
OE>>шение передачи стека имени
OE>>вызова аргументов
OE>>
OE>>
OE>>__stdcallСправа Вызванная К имени функции Используется почти
OE>> налево функция сама добавляется пре- всеми системными
OE>> может удаляет свои фикс из символа функциями;
OE>> наоборот - ? аргументы из подчеркивания Используется по
OE>> стека. и суффикс из сим- умолчанию внут-
OE>> вола @, за которым ренними функциями
OE>> следует десятич- Visual Basic
OE>> ный размер списка
OE>> аргументов в бай-
OE>> тах: например,
OE>> _Foo@12
OE>>
С каких пор
__stdcall
Microsoft Specific
The __stdcall calling convention is used to call Win32 API functions. The callee cleans the stack, so the compiler makes vararg functions __cdecl. Functions that use this calling convention require a function prototype.
Argument-passing order Right to left.
Argument-passing convention By value, unless a pointer or reference type is passed.
Stack-maintenance responsibility Called function pops its own arguments from the stack.
Name-decoration convention An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number of bytes (in decimal) in the argument list. Therefore, the function declared as int func( int a, double b ) is decorated as follows: _func@12
Case-translation convention None
The /Gz compiler option specifies __stdcall for all functions not explicitly declared with a different calling convention.
OE>Согла- Порядок Очистка Расширение Примечания
OE>шение передачи стека имени
OE>вызова аргументов
OE>
OE>
OE>__cdecl Справа Аргументы из К имени функции Применяется
OE> налево стека удаляются добавляется пре- по умолчанию
OE> вызывающей фикс из символа функциями
OE> функцией. подчеркивания: C и C++
OE> Только это сог- например, _Foo
OE> лашение поддер-
OE> живает перемен-
OE> ное число
OE> аргументов
OE> функций.
OE>__stdcallСправа Вызванная К имени функции Используется почти
OE> налево функция сама добавляется пре- всеми системными
OE> может удаляет свои фикс из символа функциями;
OE> наоборот - ? аргументы из подчеркивания Используется по
OE> стека. и суффикс из сим- умолчанию внут-
OE> вола @, за которым ренними функциями
OE> следует десятич- Visual Basic
OE> ный размер списка
OE> аргументов в бай-
OE> тах: например,
OE> _Foo@12
OE>
__cdecl и Co
От:
Аноним
Дата:
04.08.05 23:59
Оценка:
Здрасьте. Где бы мне почитать про все эти __cdecl, __declspec и т. д. Можно ссылку, плиз?
05.08.05 13:33: Перенесено модератором из 'C/C++' — Хитрик Денис
Re: __cdecl и Co
От:
Аноним
Дата:
05.08.05 00:01
Оценка:
Здравствуйте, Аноним, Вы писали:
А>Здрасьте. Где бы мне почитать про все эти __cdecl, __declspec и т. д. Можно ссылку, плиз?
Здравствуйте, gbt, Вы писали:
gbt>Hello, , you wrote:
А>>>Здрасьте. Где бы мне почитать про все эти __cdecl, __declspec и т. д. Можно ссылку, плиз? >> PS Только не мсдн...
gbt>Это же microsoft-specific, поэтому достаточно внятно и подробно описано в gbt>MSDN... gbt>
Не то чтобы это было Microsoft-specific, это Windows-specific. В Win32 API большинство функций __stdcall, но изредка встречаются __cdecl (например, wsprintf), поэтому всякий Windows-программист должен представлять, что такое calling convention и чем ему грозит неправильное указание соглашения (как правило, срывом стека).
Hello, Centaur, you wrote:
gbt>>Это же microsoft-specific, поэтому достаточно внятно и подробно описано в gbt>>MSDN... gbt>>
> Не то чтобы это было Microsoft-specific, это Windows-specific. В Win32 API > большинство функций __stdcall, но изредка встречаются __cdecl (например, > wsprintf), поэтому всякий Windows-программист должен представлять, что такое > calling convention и чем ему грозит неправильное указание соглашения (как > правило, срывом стека).
)))))))
А windows по-вашему не Microsoft-specific ?
)))))))
Кодт wrote:
> Здравствуйте, Centaur, Вы писали: > > C>Не то чтобы это было Microsoft-specific, это Windows-specific. В Win32 API большинство функций __stdcall, но изредка встречаются __cdecl (например, wsprintf), поэтому всякий Windows-программист должен представлять, что такое calling convention и чем ему грозит неправильное указание соглашения (как правило, срывом стека). > > Это не Windows-specific, а x86-specific. > Разница между конвенциями cdecl (стек очищает вызывающая сторона) и stdcall (стек очищает вызываемая функция) связана с тем, что у i8086 есть две команды возврата — RET и RET n. Вторая экономит код (не нужно во всех местах расставлять ADD ESI,n). Однако язык Си, который поддерживает функции с переменным числом параметров, не может обойтись более компактным stdcall'ом.
Как раз из приведенных ссылок на blog Raymond Chen:
Nearly all Win16 functions are exported as Pascal calling convention. The callee-clean convention saves three bytes at each call point, with a fixed overhead of two bytes per function. So if a function is called ten times, you save 3*10 = 30 bytes for the call points, and pay 2 bytes in the function itself, for a net savings of 28 bytes. It was also fractionally faster. On Win16, saving a few hundred bytes and a few cycles was a big deal.
Здравствуйте, Кодт, Вы писали:
К>Разница между конвенциями cdecl (стек очищает вызывающая сторона) и stdcall (стек очищает вызываемая функция) связана с тем, что у i8086 есть две команды возврата — RET и RET n. Вторая экономит код (не нужно во всех местах расставлять ADD ESI,n). Однако язык Си, который поддерживает функции с переменным числом параметров, не может обойтись более компактным stdcall'ом.
Кстати у cdecl есть еще то виртуальное преимущество что компилятор может повторно использовать
переменные запихнутые в стек для следующих вызовов (т.е. потенциально делать более быстрый код).
Однако на практике, много времени занимаясь анализом бинарного кода, я видел такое очень редко
Здравствуйте, fay, Вы писали:
fay>Здравствуйте, Warturtle, Вы писали:
W>>Здравствуйте, Odi$$ey, Вы писали:
OE>>>
OE>>>Согла- Порядок Очистка Расширение Примечания
OE>>>шение передачи стека имени
OE>>>вызова аргументов
OE>>>
OE>>>
OE>>>__stdcallСправа Вызванная К имени функции Используется почти
OE>>> налево функция сама добавляется пре- всеми системными
OE>>> может удаляет свои фикс из символа функциями;
OE>>> наоборот - ? аргументы из подчеркивания Используется по
OE>>> стека. и суффикс из сим- умолчанию внут-
OE>>> вола @, за которым ренними функциями
OE>>> следует десятич- Visual Basic
OE>>> ный размер списка
OE>>> аргументов в бай-
OE>>> тах: например,
OE>>> _Foo@12
OE>>>
fay>С каких пор
fay>...
Пардон муа, с __pascal перепутал похоже.
Здравствуйте, gbt, Вы писали:
gbt>__declspec ИМХО можно встретить только в дешевых книжках аля "Visual C++ за 15 gbt>дней", но такое лучше не читать...
А насколько дешовым Вы считаете __declspec( naked ) ?
Hello, srggal, you wrote:
gbt>>__declspec ИМХО можно встретить только в дешевых книжках аля "Visual C++ за 15 gbt>>дней", но такое лучше не читать...
> А насколько дешовым Вы считаете __declspec( naked ) ?
Здравствуйте, Odi$$ey, Вы писали:
OE>Здравствуйте, <Аноним>, Вы писали:
А>>Здрасьте. Где бы мне почитать про все эти __cdecl, __declspec и т. д. Можно ссылку, плиз?
OE>
OE>this Справа налево. Аргументы из Нет. Автоматически приме-
OE> Параметр стека удаляются няется к методам клас-
OE> this вызывающей сов C++, пока вы не ука-
OE> передается функцией. жете стандартное согла-
OE> в регистре шение вызова. При
OE> ECX. объявлении методов COM
OE> используется стандартное
OE> соглашение вызова.
OE>
Хотя отдельные функции так объявлять нельзя, если какой=то нехватает компилятор выдаёт название __thiscall. Уже которая версия, так что название можно считать устоявшимся.
Здравствуйте, gbt, Вы писали:
gbt>Hello, srggal, you wrote:
gbt>>>__declspec ИМХО можно встретить только в дешевых книжках аля "Visual C++ за 15 gbt>>>дней", но такое лучше не читать...
>> А насколько дешовым Вы считаете __declspec( naked ) ?
gbt>Такое вообще нигде кроме MSDN не прочитаешь.
Смотрите на примео использования:
//! Конвертация заданного байта в его строково-шестнадцатиричное представление.
/*!
* \param[in,out] al обрабатываемый байт
*
* \retval ah строково-шестнадцатиричное представление старшей тетрады;
* \retval al строково-шестнадцатиричное представление младшей тетрады.
*
* \warning
* ЭТО НЕТРИВИАЛЬНАЯ ФУНКЦИЯ.
*
* Пролог и эпилог реализованы вручную с целью уменьшения накладных
* расходов на вызов (часть стандартного пролога и эпилога
* закомментирована с демонстрационной целью ).
*
* Накладные расходы: ближний call.
*
* На самом деле это не С++ функция, а скорее подпрограмма ассемблера.
*
* \note
* Обработка двух тетрад за раз ( дублирующийся код ) производится исключительно
* с целью уменьшения накладных расходов (можно было вынести отдельно).
*/void __declspec( naked ) cvtByte2strhex( void )
{
__asm
{
// ---------- prolog
//push ebp ; Save ebp
//mov ebp, esp ; Set stack frame pointer
//sub esp, __LOCAL_SIZE
mov ah, al
// ---------- обработка первого символа
shr ah, 4
cmp ah, 10
jb less_then_10_1
add ah, 'A' - 10
jmp next_1
less_then_10_1:
or ah, '0'
next_1:
// ---------- обработка второго символа
and al, 0x0F // обнуление старшей тетрады
cmp al, 10
jb less_then_10_2
add al, 'A' - 10
jmp next_2
less_then_10_2:
or al, '0'
next_2:
// ---------- epilog
//mov esp, ebp ; Restore stack pointer
//pop ebp ; Restore ebp
ret ; Return from function
}
}
//! Конвертация заданного слова содержащего строково-шестнадцатиричное значение в двоичное значение.
/*!
* \param[in,out] ax обрабатываемое слово
*
* \retval al двоичное значение.
*
* \warning
* ЭТО НЕТРИВИАЛЬНАЯ ФУНКЦИЯ.
*
* Пролог и эпилог реализованы вручную с целью уменьшения накладных
* расходов на вызов (часть стандартного пролога и эпилога
* закомментирована с демонстрационной целью )
*
* Накладные расходы: ближний call
*
* На самом деле это не С++ функция, а скорее подпрограмма ассемблера.
*
* \note
* Обработка слова ( дублирующийся код ) производится исключительно
* с целью уменьшения накладных расходов (можно было вынести отдельно).
*/void __declspec( naked ) cvtstrhexword2byte( void )
{
__asm
{
// ---------- prolog
//push ebp ; Save ebp
//mov ebp, esp ; Set stack frame pointer
//sub esp, __LOCAL_SIZE
// ---------- обработка первого символа
cmp al, '9'
jbe only_digits1
and al, 0xDF // Принудительное преобразование в UpperCase
sub al, 7
only_digits1:
sub al, '0'
shl al, 4
// ---------- обработка второго символа
cmp ah, '9'
jbe only_digits2
and ah, 0xDF // Принудительное преобразование в UpperCase
sub ah, 7
only_digits2:
sub ah, '0'// ---------- получить в AL - сконвертированное значение байта
or al, ah
// ---------- epilog
//mov esp, ebp ; Restore stack pointer
//pop ebp ; Restore ebp
ret ; Return from function
}
}
//! Преобразование числового значения в строково-шестнадцатиричное.
/*!
*
* \param[in] sourceVal конвертируемое значение;
* \param[in,ou] pDest буфер принимающий сконвертированное значение;
* \param[in] sizeOfSourceVal размер конвертируемого значения.
*
* \return pDest
*
* Данная реализация поддерживает преобразование числовых значений следующих типов:
* - char;
* - short;
* - long.
*
* \note
* Значение третьего аргумента (\e sizeOfSourceVal) выводится автоматически.
* Это крайне необходимо, поскольку я не нашел другого способа получения размера \e T.
*
* \sa
* cvtFromStrHex2Value()
*/template< typename T >
char* cvtValue2StrHex( const T& sourceVal, char* pDest, const size_t sizeOfSourceVal = sizeof( T ) )
{
__asm
{
push ecx
push edx
push esi
// EDX - исходное значение
mov ecx, DWORD PTR sourceVal
mov edx, [ecx]
// ECX - переменная цикла, равна кол-ву байт источника
mov ecx, sizeOfSourceVal
// ESI - адрес результата
mov esi, pDest
main_loop:
mov al, dl
call cvtByte2strhex
// ----------
shr edx, 8
// HACK: инструкция mov - не изменяет флаг ZF
dec cx
// ---------- сохранение результатов
mov [esi + ecx * 2 + 1], al
mov [esi + ecx * 2], ah
jnz main_loop
pop esi
pop edx
pop ecx
mov eax, pDest
}
}
//! Преобразование строково-шестнадцатиричного значения в двоичное.
/*!
* \param[in] pSourse буфер содержащий конвертируемое значение;
* \param[in,out] destVal двоичное(сконвертированное) значение;
* \param[in] sizeOfDestVal размер двоичного(сконвертированного) значения.
*
* \return destVal
*
* Данная реализация поддерживает преобразование в значения следующих типов:
* - char;
* - short;
* - long.
*
* \note
* Регистр символов - значения не имеет.
*
* \warning
* Функция не производит проверки на правильность значений:
* в случае наличия в строке символа не принадлежащего
* диапазону '0'-'9' && 'A'-'F' - последствия не определены.
*
* \note
* Значение третьего аргумента (\e sizeOfDestVal) выводится автоматически.
* Это крайне необходимо, поскольку я не нашел другого способа получения размера \e T.
*
* \sa
* cvtValue2StrHex()
*/template< typename T >
T& cvtFromStrHex2Value( const char* pSourse, T& destVal, const size_t sizeOfDestVal = sizeof( T ) )
{
__asm
{
// Сохраняем регистры
push ecx
push edx
push esi
// ECX - переменная цикла, равна кол-ву байт результата
mov ecx, sizeOfDestVal
// EDX - адрес результата
mov edx, DWORD PTR destVal
// ESI - адрес обрабатываемого символа
mov esi, pSourse
main_loop:
// ---------- сдвиг результата на 8 бит, в зависимости от типа результата
cmp sizeOfDestVal, 1
jne dest_not_char
shl BYTE PTR [edx], 8 // результат типа 'char'
jmp after_shift_dest
dest_not_char:
cmp sizeOfDestVal, 2
jne dest_not_short
shl WORD PTR [edx], 8 // результат типа 'short'
jmp after_shift_dest
dest_not_short:
shl DWORD PTR [edx], 8 // результат типа 'long'
after_shift_dest:
// ---------- загрузить в AX обрабатываемое слово
mov ax, [ esi ]
add si, 2
call cvtstrhexword2byte
// ---------- накопление результата
or BYTE PTR [edx],al
loop main_loop
// Поместить в EAX destVal
mov eax, edx
// Восстанавливаем регистры
pop esi
pop edx
pop ecx
}
}
Hello, srggal, you wrote:
gbt>>>>__declspec ИМХО можно встретить только в дешевых книжках аля "Visual C++ за 15 gbt>>>>дней", но такое лучше не читать...
>>> А насколько дешовым Вы считаете __declspec( naked ) ?
gbt>>Такое вообще нигде кроме MSDN не прочитаешь.
> Смотрите на примео использования:
Ну, посмотрел. А зачем мне он. Я лично знаю, что naked фунция — это функция без
автоматически генерируемого пролога и эпилога. Вычитал я это в свое время в
MSDN'е, поэтому и другим советовал почитать в MSDN.
Вот это — штука уже точно специфическая для MSVC, поэтому, как я уже говорил
лучше не заморачиваться с поиском альтернативного источника информации и
спокойно смотреть MSDN.
Может это и можно встретить в какой-то книжке, но там скорее всего не будет так
подробно расписано, как в MSDN.
Поэтому я не понял, зачем вы привели пример использования naked...
Здравствуйте, Warturtle, Вы писали:
W>Здравствуйте, fay, Вы писали:
fay>>Здравствуйте, Warturtle, Вы писали:
W>>>Здравствуйте, Odi$$ey, Вы писали:
OE>>>>
OE>>>>Согла- Порядок Очистка Расширение Примечания
OE>>>>шение передачи стека имени
OE>>>>вызова аргументов
OE>>>>
OE>>>>
OE>>>>__stdcallСправа Вызванная К имени функции Используется почти
OE>>>> налево функция сама добавляется пре- всеми системными
OE>>>> может удаляет свои фикс из символа функциями;
OE>>>> наоборот - ? аргументы из подчеркивания Используется по
OE>>>> стека. и суффикс из сим- умолчанию внут-
OE>>>> вола @, за которым ренними функциями
OE>>>> следует десятич- Visual Basic
OE>>>> ный размер списка
OE>>>> аргументов в бай-
OE>>>> тах: например,
OE>>>> _Foo@12
OE>>>>
fay>>С каких пор
fay>>... W>Пардон муа, с __pascal перепутал похоже.
Надо же — опять не угадал...
The __pascal, __fortran, and __syscall calling conventions are no longer supported.
Здравствуйте, fay, Вы писали:
W>>Пардон муа, с __pascal перепутал похоже.
fay>Надо же — опять не угадал...
fay>
fay>The __pascal, __fortran, and __syscall calling conventions are no longer supported.
fay>Они переводятся в stdcall
Представь себе — я тоже удосужился это прочитать и все равно настаиваю, что теперь угадал . Вот, к примеру, цитата из книжки, ссылку на которую приводили раньше:
Pascal (__pascal)
Pascal does not support functions with a variable number of parameters, so it can use the callee-clean convention. Parameters are pushed from left to right, because, well, it seemed the natural thing to do.