Форум программистов, компьютерный форум, киберфорум
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
Рейтинг: Рейтинг темы: голосов - 89, средняя оценка - 4.75
Ze
3 / 3 / 0
Регистрация: 30.10.2010
Сообщений: 12
#1

Реализация __stdcall, __cdecl - C++

30.10.2010, 19:07. Просмотров 12508. Ответов 14
Метки нет (Все метки)

Всем добрый день!
Изучаю COM средствами C++. Смысл спецификаторов, указанных мною в заголовке мне понятен, но я хочу для понимания знать, "как что" они реализовываются. Информации по данному вопросу я найти не смог (допускаю, что не там или не так искал) и начал подозревать, что это ключевые слова самого C++, однако Qt, например, об __stdcall говорит следующее:

#define __stdcall __attribute__((__stdcall__))

то есть, это все же не "зарезервированное слово" языка C++. Буду благодарен, если кто-то мне объяснит, чем эти спецификаторы является в терминологии C++.
1
Лучшие ответы (1)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
30.10.2010, 19:07
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Реализация __stdcall, __cdecl (C++):

Работа с TASM в С++, через cdecl - Assembler
Нужно написать функцию strcat на АСМе, при этом писать в MSVS 2008 сишный код с вызовом АСМа через cdecl-соглашение. Наработка есть, но...

Как правильно получить PAnsiChar из cdecl-функции - Delphi
Добрый день. Есть функция из dll: function LoCase(CStr: PAnsiChar): PAnsiChar; cdecl; external 'caseudf_from_source.dll'; Долго не мог...

Конфликт forward и stdcall в dll-библиотеке - Delphi
Добрый день. Есть некая библиотека library S_dll; uses ...; {$R *.res} var ... function LInit:Integer; stdcall;

Передача структуры данных в DLL (stdcall) - Delphi
Хочу передать данные следующей структуры в DLL: Поле1: Символьный тип, длинной 15 букв (Pole_1: String) Поле2: "Циферный" тип, длинной...

Используя соглашение stdcall вызвать функцию на ассемблере - Assembler
надо используя соглашения stdcall вызвать функцию на ассемблере и используя соглашения cdecl и fastcall вызвать функции с программы на си ...

Правильный формат записи команды RET при использовании stdcall - Assembler
Здравствуйте! Имеется вопрос по процедурам. Если в самом начале кода написано: .386 .model flat,stdcall option casemap:none ...

Работа с TASM в С++, через cdecl - Assembler
Нужно написать функцию strcat на АСМе, при этом писать в MSVS 2008 сишный код с вызовом АСМа через cdecl-соглашение. Наработка есть, но...

Как правильно получить PAnsiChar из cdecl-функции - Delphi
Добрый день. Есть функция из dll: function LoCase(CStr: PAnsiChar): PAnsiChar; cdecl; external 'caseudf_from_source.dll'; Долго не мог...

Конфликт forward и stdcall в dll-библиотеке - Delphi
Добрый день. Есть некая библиотека library S_dll; uses ...; {$R *.res} var ... function LInit:Integer; stdcall;

Передача структуры данных в DLL (stdcall) - Delphi
Хочу передать данные следующей структуры в DLL: Поле1: Символьный тип, длинной 15 букв (Pole_1: String) Поле2: "Циферный" тип, длинной...

Используя соглашение stdcall вызвать функцию на ассемблере - Assembler
надо используя соглашения stdcall вызвать функцию на ассемблере и используя соглашения cdecl и fastcall вызвать функции с программы на си ...

Правильный формат записи команды RET при использовании stdcall - Assembler
Здравствуйте! Имеется вопрос по процедурам. Если в самом начале кода написано: .386 .model flat,stdcall option casemap:none ...

Работа с TASM в С++, через cdecl - Assembler
Нужно написать функцию strcat на АСМе, при этом писать в MSVS 2008 сишный код с вызовом АСМа через cdecl-соглашение. Наработка есть, но...

Как правильно получить PAnsiChar из cdecl-функции - Delphi
Добрый день. Есть функция из dll: function LoCase(CStr: PAnsiChar): PAnsiChar; cdecl; external 'caseudf_from_source.dll'; Долго не мог...

Конфликт forward и stdcall в dll-библиотеке - Delphi
Добрый день. Есть некая библиотека library S_dll; uses ...; {$R *.res} var ... function LInit:Integer; stdcall;

Передача структуры данных в DLL (stdcall) - Delphi
Хочу передать данные следующей структуры в DLL: Поле1: Символьный тип, длинной 15 букв (Pole_1: String)

Работа с TASM в С++, через cdecl - Assembler
Нужно написать функцию strcat на АСМе, при этом писать в MSVS 2008 сишный код с вызовом АСМа через cdecl-соглашение. Наработка есть, но...

Как правильно получить PAnsiChar из cdecl-функции - Delphi
Добрый день. Есть функция из dll: function LoCase(CStr: PAnsiChar): PAnsiChar; cdecl; external 'caseudf_from_source.dll'; Долго не мог...

Конфликт forward и stdcall в dll-библиотеке - Delphi
Добрый день. Есть некая библиотека library S_dll; uses ...; {$R *.res} var ... function LInit:Integer; stdcall;

Передача структуры данных в DLL (stdcall) - Delphi
Хочу передать данные следующей структуры в DLL: Поле1: Символьный тип, длинной 15 букв (Pole_1: String) Поле2: "Циферный" тип, длинной...

Используя соглашение stdcall вызвать функцию на ассемблере - Assembler
надо используя соглашения stdcall вызвать функцию на ассемблере и используя соглашения cdecl и fastcall вызвать функции с программы на си ...

Правильный формат записи команды RET при использовании stdcall - Assembler
Здравствуйте! Имеется вопрос по процедурам. Если в самом начале кода написано: .386 .model flat,stdcall option casemap:none ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
14
Nick Alte
Эксперт С++
1642 / 1014 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
30.10.2010, 19:32 #2
Это специфические для компилятора расширения. Конвенции вызова не прописаны в стандарте C++, но нужны для жизни, так что разные компиляторы могут определять конвенции разными словами.
1
Ze
3 / 3 / 0
Регистрация: 30.10.2010
Сообщений: 12
30.10.2010, 19:36  [ТС] #3
То есть, их можно понимать как что-то вроде "директив компилятора"?
1
Nick Alte
Эксперт С++
1642 / 1014 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
30.10.2010, 20:57 #4
Не совсем, их можно понимать как расширение языка, специфическое для компилятора - это ключевые слова, не описанные в стандарте.
2
Ze
3 / 3 / 0
Регистрация: 30.10.2010
Сообщений: 12
03.11.2010, 17:06  [ТС] #5
Прочитал некоторое количество довольно неплохих, на мой взгляд, книг по C++. Вот первая, в которой подобные конструкции встретились, и даются в примерах без особого объяснения...
Вот тоже из этой серии:

C++
1
2
3
BOOL APIENTRY DllMain(HANDLE hModule,
                      DWORD dwReason,
                      void* lpReserved)
BOOL - понятно, тип, DllMain - имя функции. Но что такое APIENTRY? Тоже специальный "тип" какой-то? Имя какой-то переменной?
Вот __stdcall называют "спецификатором". Что такое спецификатор? Почему в книгах по C++ вообще такого термина нет? Я хочу понять не "для чего это надо", а "что это такое". Понять синтаксис. Где еще, например, можно подобное вставлять кроме как перед функциями... Суть, короче говоря.

Может быть есть хорошие источники информации именно по этому вопросу...
Спасибо.
0
KpeHDeJIb
56 / 56 / 3
Регистрация: 31.10.2010
Сообщений: 103
03.11.2010, 17:15 #6
Цитата Сообщение от Ze Посмотреть сообщение
#define __stdcall __attribute__((__stdcall__))
Ну это для GCC, вообще по поводу конвенций вызова функции читай тут http://en.wikipedia.org/wiki/Calling_convention
0
Nick Alte
Эксперт С++
1642 / 1014 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
03.11.2010, 21:16 #7
APIENTRY - спецификатор типа вызова. Делается обычным макросом в составе <windows.h> навроде
C++
1
#define APIENTRY __stdcall
под конкретную версию компилятора - чтобы одинаково описывать функции для разных компиляторов. Очевидно, что и про BOOL, и про APIENTRY и даже про DllMain пишут в MSDN.
1
Ze
3 / 3 / 0
Регистрация: 30.10.2010
Сообщений: 12
04.11.2010, 10:24  [ТС] #8
То, что APIENTRY это спецификатор вызова, и что
C++
1
#define APIENTRY __stdcall
я знаю.
BOOL и даже DllMain трудностей в понимании не составляют.
Вопрос был больше о самих спецификаторах. Насколько я вижу, у них даже имена не однотипны, чтобы они в синтаксисе выделялись как-то (syscall, pascal, cdecl, ...)
Спасибо за ответы.
Просто меня удивило то, что в том же C++ PRIMER Липпмана, где детально разбираются довольно серьезные нюансы языка, о типах вызовов и способах их задания ни слова не написано, а его называют одним из лучших учебников...
0
Nick Alte
Эксперт С++
1642 / 1014 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
04.11.2010, 12:46 #9
Конвенции вызова имеют мало отношения к языку, а Липпман рассказывает прежде всего о чистом незамутнённом C++. Но когда наивному программисту с пылающим взором приходится сталкиваться с реальным миром, он обнаруживает, что приходится взаимодействовать с программными модулями, написанными разными людьми и даже - о, ужас! - на других языках. Например, злые языки поговаривают, что тот же Windows был написан на паскале, откуда и спецификатор __stdcall на всём, что с этим API связано (раньше __stdcall по-свойски именовали просто pascal). Да и __cdecl тоже сиплюсплюсу не родной, а уходит корнями в Си (и вы-таки не объявите перегруженные функции с таким модификатором).
То есть, это всё лежит за пределами собственно языка и касается скорее взаимодействия с другими средствами.
1
nxnx
Формучанин
362 / 293 / 16
Регистрация: 02.11.2010
Сообщений: 1,234
04.11.2010, 13:39 #10
Соглашение вызова
3
Ze
3 / 3 / 0
Регистрация: 30.10.2010
Сообщений: 12
04.11.2010, 13:53  [ТС] #11
Спасибо за объяснения.
Я не стал бы это называть "ужасом"
("приходится взаимодействовать с программными модулями, написанными разными людьми и даже - о, ужас! - на других языках").
На мой взгляд, в книгах, претендующих на "лучшие учебники" об этом было бы полезно полезно говорить "наивному программисту с пылающим взором", несмотря на то, что это не оговаривает стандарт. Так как этот механизм только расширяет пределы практической применимости языка, ведь реальные программы не пишутся ради программирования, как Вы правильно заметили "приходится сталкиваться с реальным миром".

nxnx, спасибо, уже знаком с этой статьей.
1
Evg
Эксперт CАвтор FAQ
18374 / 6421 / 441
Регистрация: 30.03.2009
Сообщений: 17,813
Записей в блоге: 28
03.03.2011, 20:59 #12
Лучший ответ Сообщение было отмечено автором темы, экспертом или модератором как ответ
Ze, в стандарте Си++ о них ничего не написано, потому что языки программирования для того и придумывают, чтобы программист мог работать, не задумываясь о том, как реально при исполнении задачи будут передаваться параметры. А все правила передачи параметров на низком уровне описываются в программных соглашениях (ABI - application binary interface) для каждого конкретного процессора (а иногда даже на одном процессоре под разными операционными системами могут быть разные соглашения). В случае процессора x86, как мы видим, даже в рамках одной операционной системы существуют различные программные соглашения. И все эти модификаторы - это расширения исключительно компиляторов для платформы x86 (а не свойства языка Си++).

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

Например, функция с маленьким числом параметров (1-2-3 параметра). И имеется целая цепочка вызовов таких функций, при этом функции являются короткими. Ситуация характерна для всяких обработчиков ситуаций, когда функция-обработчик реально представляет собой прокладку для вызова другой функции, в том числе и какой-нибудь функции из базового класса в цепочке наследований. При таком раскладе если мы будем передавать параметры через стек, каждый раз его настраивая в функции или в точке вызова, то для прохождения такой цепочки вызовов коротких функций будут сравнительно большие затраты: очень много работы со стеком, причём работы тупой, потому что мы фактически будем заниматься только копированием параметров. Для таких случаев был заведён тип программного соглашения fastcall, при котором аргументы передаются на регистрах. А потому такая цепочка отработает очень быстро, не залезая в память (что важно на современных процессорах, потому как доступы в память являются "медленными"). Более того, если функция просто транзитно передаёт аргументы в другую функцию, то их даже копировать не надо, потому что входящие и исходящие параметры передаются на тех же самых регистрах.

Всякие другие типы соглашений, видимо, разрабатывались ещё для каких-то частных случаев. Каждый тип соглашения может быть использован в любой функции, но, использованный не по делу, может привести к замедлению кода, а не к ускорению.

Ну вот что-то типа того

Добавлено через 1 минуту
Цитата Сообщение от Nick Alte Посмотреть сообщение
Конвенции вызова
Вот это я называл словами "программные соглашения". Так, на всякий случай, тем кто привык к другой терминологии
14
Kastaneda
Jesus loves me
Эксперт С++
4697 / 2901 / 238
Регистрация: 12.12.2009
Сообщений: 7,385
Записей в блоге: 2
Завершенные тесты: 1
03.03.2011, 21:13 #13
Цитата Сообщение от Evg Посмотреть сообщение
Но могу предположить, что делается это для ускорении кода.
Или по другим причинам. После вызова ф-ции кто-то должен выровнять стек. По соглашению С (Си) этим занимается вызывающая процедура, а по соглашению PASCAL - вызываемая. Соглашение С полезено когда неизвестно, как много паpаметpов будут пеpеданны функции, как напpимеp, в случае printf(), когда функция не может знать заpанее, сколько паpаметpов будут положены в стек, так что она не может его уpавнять.
1
Evg
Эксперт CАвтор FAQ
18374 / 6421 / 441
Регистрация: 30.03.2009
Сообщений: 17,813
Записей в блоге: 28
03.03.2011, 21:24 #14
Kastaneda, я слишком плохо знаю i386, чтобы вести об этом разговор У всех нормальных людей есть два указателя стека - frame pointer и stack pointer (указывают на начало и конец текущего фрейма). Входящие параметры отсчитываются от frame pointer, исходящие - от stack pointer. При исполнении операции вызова автоматически или полуавтоматически происходит замета текущего sp на fp, а вызванная функция выделает себе нужное место и формирует новый sp. И только на i386 как всегда всё через ж...у сделано, потому что на дворе уже третье тысячелетие, а у них до сих пор регистров раз два и обчёлся
2
Ze
3 / 3 / 0
Регистрация: 30.10.2010
Сообщений: 12
04.03.2011, 11:55  [ТС] #15
Evg, хорошее объяснение.
0
04.03.2011, 11:55
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.