更好地理解extern "C"函数

6

我只是想更深入地了解extern C函数。

据我所知,extern C函数总是你尝试从已经编译的应用程序中调用的函数。可以是可执行文件、静态库或动态库。

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

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

所以我的问题是...

我的理解是否正确?

它总是必须是C函数指针吗?

为什么每个函数都必须使用typedef?

我假设当你使用GetProcAddress()时,你正在那个特定应用程序的堆上分配内存,而不是从调用它的堆中分配。因此,你必须在那个堆中释放它吗?


你看到这个问题了吗?https://dev59.com/SXVD5IYBdhLWcg3wKYP- - Jujjuru
5个回答

4
extern "C"有两个含义。首先,它声明函数的符号名称不会进行"C++名字修饰",其次,它告诉编译器该函数是使用C调用约定而不是PASCAL调用约定进行调用的。这种差异与返回地址何时被推送到堆栈有关。使用错误的调用约定将导致应用程序崩溃。
此声明是针对编译器而不是链接器的。因此,extern C函数可以存在于您自己的模块或二进制库中:实际字节的函数实现来源由链接器解析。如果函数签名声明为常规C++函数而不是extern C,则编译器将对符号名称进行名称修饰以从函数签名中编码类型信息。这将使其与其他C++编译器生成的目标代码不兼容。因此,创建extern C函数允许您以二进制形式在编译器之间共享代码。请注意,您无法以这种方式公开成员函数,只能使用旧式C函数。

1

它不一定是函数指针。您可以正常指定函数声明并在其前缀中添加 extern "C",如 一些 Microsoft 示例 所示。

如果您使用 GetProcAddress(),则不会分配任何内存。您只需获取已经加载到内存中的 DLL 中包含的函数的内存地址(使用 LoadLibrary(),大概是这样)。

即使使用函数指针(例如 GetProcAddress 返回的那些),您也不必使用 typedef,只是没有 typedef 的代码看起来相当丑陋。始终很难确定要编写什么。我认为应该是这样的:

void (*pReleaseDevice)() = (void (__cdecl *)(void))GetProcAddress(hInstance, "ReleaseDevice");

请注意,__cdecl 是微软特定的。 - Billy ONeal
2
@Billy ONeal:GetProcAddress和Visual Studio也是如此。 - dreamlax
GetProcAddress 不是 Microsoft 特定的,但它是 Windows 特定的。例如,我可以在 MinGW 中使用 GetProcAddress,但不能使用 __cdecl。 - Billy ONeal
顺便说一下,我点了个赞,我觉得那是一个重要的注释。 - Billy ONeal

0

extern "C" {} 是 C++ 的一种约定,用于声明封闭的函数是 C 函数而不是 C++ 函数。C++ 有一个略微不同的命名约定,与 C 冲突。如果您有一个用 C 编写的库,并想在 C++ 程序中使用它,则必须使用 extern "C" {} 来让编译器知道这些是 C 函数。如果该库是用 C++ 编写的,我认为 extern "C" {} 将导致错误。

请注意,extern 具有多个含义--此特定情况是 C++ 的一种约定,与 extern 的其他用途无关。例如,

extern int count;

在编程中,"extern "C" {}" 与 "has a completely different meaning" 完全不同。

typedef 与 extern "C" {} 问题是分开的。typedef 允许您为常见类型创建更有意义的别名。例如,声明结构体通常是冗长的过程。我可以使用 typedef 来缩短它:

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

因此,在您的示例中,HRESULT 实际上是 (*CREATEDEVICE)() 的别名,尽管我认为您必须将其放在函数之前(而不是之后)。

0

指定 extern "C" 链接的一个重要方面是函数名称不会被编码,这是 C++ 名称的默认设置。

为了使您的库函数能够使用 GetProcAddress 加载,您需要将函数添加到 .def 文件 中,使用 __declspec(dllexport) 或使用 extern "C"


0

依次回答:

  • extern "C" 函数用于在 C++ 中与 C 进行互操作。使用它们的后果是 C 代码可以调用该函数。由于 Windows API 是一个 C API,因此所有函数都是 extern "C",以确保 C 和 C++ 代码都可以使用该 API。

  • 为了使 c++ 程序与其他语言(包括 C)进行互操作,惯例上使用 extern "C" 导出函数。这就是为什么很多 dll 代码都这样做的原因。但这并不是技术要求。

  • 所以,它不必是 C 函数指针。

  • 您也不必使用 typedef。

提供的示例代码来自一个头文件,该头文件将 DLL 的导出发布两次 - 一次作为一组 extern "C" 方法,以便可以静态链接 DLL。另一次作为一组函数指针类型,以便可以动态加载 DLL,并使用 GetProcAddress 使用函数指针类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接