难以理解沿函数指针的 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的主要内容,如果未能解决你的问题,请参考以下文章