C 将空指针(数据)强制转换为函数指针

C 将空指针(数据)强制转换为函数指针,c,windows,pointers,C,Windows,Pointers,我知道以前有人问过这个问题,但我在这里看到的案例都不是这样的。 我在运行时导入一些API函数,这些函数的一般声明如下: // Masks for UnmapViewOfFile and MapViewOfFile typedef BOOL (WINAPI *MyUnmapViewOfFile)(LPCVOID); typedef LPVOID (WINAPI *MyMapViewOfFile)(HANDLE, DWORD, DWORD, DWORD, SIZE_T); // Declarati

我知道以前有人问过这个问题,但我在这里看到的案例都不是这样的。 我在运行时导入一些API函数,这些函数的一般声明如下:

// Masks for UnmapViewOfFile and MapViewOfFile
typedef BOOL (WINAPI *MyUnmapViewOfFile)(LPCVOID);
typedef LPVOID (WINAPI *MyMapViewOfFile)(HANDLE, DWORD, DWORD, DWORD, SIZE_T);

// Declarations
MyUnmapViewOfFile LoadedUnmapViewOfFile;
MyMapViewOfFile LoadedMapViewOfFile;
然后,我调用一个通用的“load”函数,它在其中调用GetProcAddress,以从正确的DLL获取导出函数的地址。该地址以空**返回。此void**是通用加载中的参数之一,类似于:

int GenericLoad(char* lib, void** Address, char* TheFunctionToLoad)
typedef INT_PTR (FAR WINAPI *FARPROC)();
我把这个函数称为:

void *Address;
GenericLoad("kernel32.dll", &Address, "UnmapViewOfFile");
LoadedUnmapViewOfFile = (MyUnmapViewOfFile) Address;
或者类似的东西。 现在,编译器当然会抱怨试图将数据void*强制转换为函数指针。那我该怎么做呢

我读过无数的网站和各种令人讨厌的类型转换,所以如果您能在解释中添加代码,我将不胜感激

谢谢
Jess

正确的代码应该是这一行:

GenericLoad("kernel32.dll", (void**)&LoadedUnmapViewOfFile, "UnmapViewOfFile");

这里所做的基本上是这样的:指针变量的地址(您希望将函数的地址放在其中的那个)被传递给GenericLoad—这基本上是它所期望的。void**表示“给我你指针的地址”。所有的类型铸造是一个魔术围绕它。C不允许指定“指向任何函数指针的指针”,因此API作者倾向于void**。

数据地址和函数地址是不兼容的。我相信您的
地址
变量必须是函数指针,就像您的
BOOL
定义:
(WINAPI*MyUnmapViewOfFile)(LPCVOID)
一样。这种类型的声明是必需的,因为要有指向函数的指针,必须知道返回类型以及参数的类型和数量。这是因为在调用函数时,必须在堆栈上分配正确的空间量以包含这些返回值和参数

鉴于此,我认为Pavel答案中的更正是正确的(仅供参考,他的(void**)石膏是一种类型安全措施)

现在,编译器当然会抱怨试图将数据void*强制转换为函数指针

我的编译器对你的代码一点也不抱怨(再说一次,我没有任何警告——我使用了或多或少的默认选项)。这是由MS、GCC和其他编译器提供的。您能否提供更多有关您正在使用的编译器和编译器选项的详细信息,以及您看到的确切警告

也就是说,C不能保证函数指针可以毫无问题地从空指针转换到空指针,但实际上这在Windows上可以很好地工作

如果您想要符合标准的东西,您需要使用“通用”函数指针而不是空指针-C保证函数指针可以转换为任何其他函数指针并返回而不会丢失,因此无论您的平台如何,这都可以工作。这可能就是为什么Win32
GetProcAddress()
API的返回值返回一个
FARPROC
,它只是指向不带参数(或至少未指定参数)的函数的函数指针的typedef,并返回指针大小的int。类似于:

int GenericLoad(char* lib, void** Address, char* TheFunctionToLoad)
typedef INT_PTR (FAR WINAPI *FARPROC)();
FARPROC
将是Win32的“通用”函数指针。因此,您需要做的就是拥有一个类似的typedef(如果出于某种原因不想使用
FARPROC
):

虽然这比使用中间变量的方法更危险-例如,在最后一个示例中,如果您关闭了符号AND,编译器将不会给出诊断,但是在上一个示例中,如果您从
Address
参数中关闭了符号AND,它通常至少会给出警告。类似于这里的Bug#1:

现在你应该准备好了。然而,不管你怎么看,你都需要进行一些危险的铸造。即使在C++中,模板也必须在某个级别执行一个强制转换(尽管您可能能够将其隐藏在模板函数中),因为GETPro()/<代码> API不知道您正在检索的函数指针的实际类型。
还请注意,
GenericLoad()
的接口可能存在严重的设计问题—它无法管理库的生命周期。如果你的意图是不允许卸载一个库,这可能不是问题,但这是用户可能想要的,所以你应该考虑这个问题。

一种新的“PLZ发送代码”。当然,这是个骗局。我们能找到什么是骗局吗?@Neil,如果你确定的话,你能给我指一下这篇文章吗?不,我不是因为懒惰而要求代码,而是因为我不知道答案。这就是我在这里问的原因。谢谢!我试试这个,看看有没有东西坏了