难以理解沿函数指针的 typedef

Posted

技术标签:

【中文标题】难以理解沿函数指针的 typedef【英文标题】:Trouble understanding a typedef along function pointers 【发布时间】:2019-12-17 04:59:53 【问题描述】:

即使在此处阅读了有关此主题的一些答案后,我仍然很难理解以下语法的确切作用:

typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

我的猜测: 它将DllEntryProc 数据类型定义为BOOL 的别名,其中DllEntryProc 是一个指向函数的指针,该函数接受一个HINSTANCE、一个DWORD 和一个LPVOID 作为参数并返回一个WINAPI ?

上面的代码是this article 中关于如何从内存中加载DLL 的一部分。然后像这样调用该函数:

DllEntryProc entry = (DllEntryProc) someValue;
(*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0);

返回 BOOL(感谢 typedef),对吗?

【问题讨论】:

【参考方案1】:

它将DllEntryProc数据类型定义为BOOL的别名,其中DllEntryProc是一个指向一个函数的指针,该函数接受一个HINSTANCE、一个DWORD和一个LPVOID作为参数并返回一个 WINAPI BOOL

typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

DllEntryProc 是一个类似于int 的新类型,您可以像声明int 类型的变量一样声明此类型的变量。

DllEntryProc somevar;

现在您可以分配给somevar 的值应该是DllEntryProc 类型,它是指向所述类型的函数的指针。

【讨论】:

WINAPI有什么意义? WINAPI 是调用约定之一。阅读调用约定。 @NikhilCSB WINAPI 是一个预处理器宏,可解析为 __stdcall 调用约定。【参考方案2】:

当 typedef 用于函数指针时,您可以给它一个友好名称,这样可以更轻松地使用该定义创建和引用指针。示例

#include<stdio.h>
int test(int a, char b, float c)
        printf("a=%d,b=%c,c=%f\n",a,b,c);

typedef int (*test_p)(int a, char b, float c);

int main()
        test_p ptr = test;
        ptr(10, 'a', 5.5);


# gcc test.c 
# ./a.out 
a=10,b=a,c=5.500000

回到你的函数指针声明。

typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

等同于:

typedef int (*test_p)(int a, char b, float c);

这里的WINAPI 是一个计算结果为 __stdcall 的宏,__stdcall 是一个 Microsoft 特定的关键字,它指定被调用方清理堆栈的调用约定。函数的调用者和被调用者需要就调用约定达成一致,以避免损坏堆栈。

【讨论】:

【参考方案3】:

typedef 创建一个函数指针类型。简单来说,函数指针是这样工作的:

给定一个函数void func (void),指向这样一个函数的函数指针被声明为void (*ptr) (void)。同一个函数指针的typedef 可以写成:

typedef void (*ptr_t) (void);,用法ptr_t ptr;,或 typedef void ptr_t (void);,用法ptr_t* ptr

前一种风格可能是最常见的,也是 Windows API 使用的风格。就我个人而言,我发现后一种风格更清晰,因为它与对象指针一致,但这只是一个偏好问题 - 两者都可以。

现在可以使用函数指针来调用函数,使用 ptr()(*ptr)() - 它们是等效的,只是样式不同。


详细剖析typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

BOOL 是 Windows API 风格的布尔类型。 Windows API 早于 C99,因此它们使用自定义布尔类型。

WINAPI 是一个宏,它隐藏了指定函数使用的calling convention 的非标准语法。它扩展为__stdcall。简而言之,调用约定是关于调用者或被调用者是否负责堆栈参数。

在与 DLL:s 通信时使用正确的调用约定非常重要,因为 DLL:s 与语言无关,并且各种编程语言使用不同的调用约定。默认情况下,C 和 C++ 倾向于使用 __cdecl 调用约定(请参阅链接),因此在重要的情况下需要指定不同的调用约定,例如 DLL:s。

DllEntryProc 是函数指针类型的新名称。

HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved 是函数参数。在 typedef 期间使用参数名称是可选的(但很好的做法)。


至于这个类型的作用,它创建一个函数指针,指向DLL中存在的DllMain/DllEntryPoint函数(不同名称相同含义)。每个 DLL 都有这样一个函数,它是一种“构造函数”,在加载 DLL 时调用。从头开始编写自己的 DLL:s 时,您必须提供此功能(但有很多关于您不应该在 DllMain 内部执行的操作的规则)。

通常,在使用 DLL 的应用程序中,通常的做法是为每个 DLL 函数都有一个函数指针,因为用于从 DLL 获取函数的 GetProcAddress 只返回一个通用函数指针。

(*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0); 是对 DllMain/DllEntryPoint 的显式函数调用。由于它是一个函数指针,替代但等效的语法是:

entry((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0);

这可能是更清晰的风格,因为它看起来就像一个函数调用。

【讨论】:

感谢您非常详尽的解释!一旦我被允许,我会记得投赞成票;)

以上是关于难以理解沿函数指针的 typedef的主要内容,如果未能解决你的问题,请参考以下文章

typedef 返回类型(*Function)(参数表) ——typedef函数指针

了解 C 中函数指针的 typedef

typedef 的使用

C语言关于指针函数与函数指针个人理解

结构体中函数指针与typedef关键用途(函数指针)

通过理解函数指针构建回调函数来实现mosquitto源码功能