难以理解沿函数指针的 typedef

Trouble understanding a typedef along function pointers


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

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

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

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

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

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

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

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

DllEntryProc 是一种类似于 int 的新类型,您可以像声明 int.

类型的变量一样声明此类型的变量
DllEntryProc somevar;

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

  • 当 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 的宏,它是 Microsoft 特定的关键字,指定被调用方清理堆栈的调用约定。函数的调用者和被调用者需要就调用约定达成一致以避免破坏堆栈。

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 调用约定(参见 link),因此在重要的情况下需要指定不同的调用约定,例如 DLL:s.

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

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


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

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

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

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

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