лучшее понимание внешних функций "C" - PullRequest
6 голосов
/ 04 июня 2010

Я просто пытаюсь лучше понять внешние функции C.

Насколько мне известно, extern C-функция - это всегда функция, которую вы пытаетесь вызвать из приложения, которое уже скомпилировано. Либо исполняемая, статическая или динамическая библиотека.

extern "C" 
{
   HRESULT CreateDevice();
   typedef HRESULT (*CREATEDEVICE)();

   HRESULT ReleaseDevice();
   typedef HRESULT (*RELEASEDEVICE)();
}

Так что мой вопрос ...

Правильно ли мое понимание ??

Всегда ли это должен быть указатель на функцию C ?? '

Почему вы должны использовать typedef для каждой функции ??

Я предполагаю, что когда вы используете GetProcAddress (). Вы выделяете память для конкретного приложения HEAP, а не для того, из которого вы его вызываете. Поэтому вы должны освободить его и из этой кучи ??

Ответы [ 5 ]

4 голосов
/ 04 июня 2010

extern "C" имеет 2 значения. Во-первых, он объявляет, что символические имена функций не «искажены» для поддержки C ++. Во-вторых, он сообщает компилятору, что функция вызывается с использованием соглашения о вызове C, а не соглашения о вызовах PASCAL. Разница заключается в том, когда адрес возврата помещается в стек. Использование неправильного соглашения о вызовах приведет к сбою приложения.

Это объявление для компилятора, а не для компоновщика. Таким образом, внешняя функция C может существовать в ваших собственных модулях или в двоичной библиотеке: источник фактических байтов для реализации функции разрешается компоновщиком. Если сигнатура функции объявлена ​​как обычная функция C ++, а не как внешняя Си, компилятор будет манипулировать символическим именем для кодирования информации о типе из сигнатуры функции. Это сделает его несовместимым с объектным кодом, созданным другими компиляторами C ++. Поэтому создание внешней C-функции позволяет вам совместно использовать код между компиляторами в двоичной форме. Обратите внимание, что вы не можете предоставлять функции-члены таким способом, только функции в старом стиле C.

1 голос
/ 04 июня 2010

Это не обязательно должен быть указатель на функцию. Вы можете указать объявление функции обычным образом и поставить перед ним префикс extern "C", как показано в некоторых примерах Microsoft .

Если вы используете GetProcAddress(), вы не выделяете никакой памяти. Вы просто получаете адрес памяти функции внутри DLL, которая уже была загружена в память (предположительно, с LoadLibrary()).

Даже при использовании указателей на функции (например, возвращаемых GetProcAddress) у вас нет для использования typedef, просто код выглядит безобразно без него. Всегда трудно понять, что написать. Я думаю, что это будет что-то вроде:

void (*pReleaseDevice)() = (void (__cdecl *)(void))GetProcAddress(hInstance, "ReleaseDevice");
0 голосов
/ 04 июня 2010

Чтобы ответить, по порядку:

  • внешние функции "C" используются для взаимодействия с C из C ++.Их использование приводит к тому, что C-код может вызывать функцию.Поскольку Windows API является C API, все функции являются внешними "C", чтобы гарантировать, что код C и C ++ может использовать API.

  • Для того, чтобы программы на С ++ могли взаимодействовать с другимиязыки, включая C, как соглашение, функции экспортируются с использованием extern "C".Вот почему много кода DLL делает это.Однако это не техническое требование.

  • Так что нет, это НЕ обязательно должен быть указатель на функцию C.

  • Вам не нужно использоватьлибо typedef.

Приведенный пример кода взят из заголовочного файла, который дважды публикует экспорт DLL-файла - один раз как набор внешних методов "C", которые экспортируются, так что dll может бытьстатически связаны.другой - как набор типов указателей на функции, так что dll может быть загружена динамически, а типы указателей на функции используются с GetProcAddress.

0 голосов
/ 04 июня 2010

Одним из важных аспектов указания связи extern "C" является то, что имена функций не искажаются, что является значением по умолчанию для имен C ++.

Чтобы функции библиотеки могли быть загружены с помощью GetProcAddress, вам необходимо либо добавить функцию в .def файл , либо использовать __declspec(dllexport), либо использовать extern "C".

0 голосов
/ 04 июня 2010

extern "C" {} - это соглашение C ++, объявляющее, что вложенные функции являются функциями C, а не функциями C ++. C ++ имеет немного другое соглашение об именах, которое конфликтует с C. Если у вас есть библиотека, написанная на C, и вы хотите использовать ее в программе на C ++, вы должны использовать extern "C" {}, чтобы компилятор знал, что это функции C. Если библиотека была написана на C ++, я полагаю, что extern "C" {} вызовет ошибку.

Обратите внимание, что extern имеет несколько значений - этот конкретный случай является соглашением C ++ и не связан с различным использованием extern. Например,

extern int count;

имеет совершенно другое значение, чем extern "C" {}.

typedef отделен от внешней проблемы "C" {}. typedefs позволяют создавать псевдонимы для распространенных типов, которые имеют больше смысла. Например, объявление структур часто является многословным процессом. Я могу использовать typedef, чтобы сократить его:

struct mystruct {int a; int b};
typedef struct mystruct returncode;
// I can now declare a variable as type 'returncode'
returncode a;

Таким образом, в вашем примере HRESULT на самом деле является псевдонимом для (* CREATEDEVICE) (), хотя я считаю, что вы должны поставить его перед функцией (а не после).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...