cdecl или stdcall в Windows?

в настоящее время я разрабатываю библиотеку C++ для Windows, которая будет распространяться как DLL. Моя цель-максимизировать двоичную совместимость; точнее, функции в моей DLL должны использоваться из кода, скомпилированного с несколькими версиями MSVC++ и MinGW без необходимости перекомпиляции DLL. Тем не менее, я смущен тем, какое соглашение о вызовах лучше всего,cdecl или stdcall.

иногда я слышу такие заявления, как " соглашение о вызове C единственный, который гарантированно будет одним и тем же компилятором accross", который контрастирует с такими утверждениями, как "есть некоторые вариации в интерпретации cdecl, особенно в том, как возвращать значения". Это, похоже, не останавливает некоторых разработчиков библиотек (например,libsndfile выступает) использовать соглашение о вызовах C в библиотеках DLL, которые они распространяют, без каких-либо видимых проблем.

С другой стороны,stdcall соглашение о вызове, похоже, четко определено. От чего Мне сказали, что все компиляторы Windows в основном обязаны следовать ему, потому что это соглашение используется для Win32 и COM. Это основано на предположении, что компилятор Windows без поддержки Win32/COM не будет очень полезен. Многие фрагменты кода, размещенные на форумах, объявляют функции как stdcall но я не могу найти ни одного сообщения, которое ясно объясняет почему.

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

обратите внимание, что этот вопрос относится не только к "классическим" функциям, но и к вызовам виртуальных функций-членов, поскольку большинство клиентских кодов будут взаимодействовать с моей DLL через "интерфейсы", чистые виртуальные классы (следующие шаблоны, описанные, например,здесь и здесь).

3 ответов


Я просто сделал некоторое реальное тестирование (компиляция DLL и приложений с MSVC++ и MinGW, а затем смешивание их). Как оказалось, у меня были лучшие результаты с cdecl вызовах.

более конкретно: проблема с stdcall это MSVC++ искажает имена в таблице экспорта DLL, даже при использовании extern "C". Например foo становится _foo@4. Это происходит только при использовании __declspec(dllexport), не при использовании файла DEF; однако файлы DEF являются проблемой обслуживания в моем мнение, и я не хочу им пользоваться.

искажение имени MSVC++ создает две проблемы:

  • используя GetProcAddress на DLL становится немного сложнее;
  • MinGW по умолчанию не добавляет undescore к оформленным именам (например, MinGW будет использовать foo@4 вместо _foo@4), что затрудняет связывание. Кроме того, он вводит риск появления" не подчеркивающих версий " библиотек DLL и приложений, которые появляются в дикой природе несовместимо с"версиями подчеркивания".

Я пробовал cdecl соглашение: совместимость между MSVC++ и MinGW работает отлично, из коробки, и имена остаются без изменений в таблице экспорта DLL. Он даже работает для виртуальных методов.

по этим причинам cdecl является явным победителем для меня.


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

кроме того, я считаю, что управляемые языки используют соглашение stdcall по умолчанию, поэтому любой, кто использует P / Invoke на нем, должен будет явно указать соглашение о вызове если вы идете с cdecl.

Итак, если все ваши сигнатуры функций будут статически определены, я, вероятно, склонюсь к stdcall, если не cdecl.


С точки зрения безопасности,__cdecl соглашение "безопаснее", потому что это вызывающий, который должен освободить стек. Что может произойти в __stdcall библиотека заключается в том, что разработчик, возможно, забыл правильно освободить стек, или злоумышленник может ввести некоторый код, повредив стек DLL (например, путем подключения API), который затем не проверяется вызывающим. У меня нет примеров безопасности CVS, которые показывают, что моя интуиция верна.