试图理解围绕 dlsym 的语法怪癖

Trying to understand syntax quirk around dlsym

本文关键字:语法 dlsym 解围      更新时间:2023-10-16

我一直在尝试学习一些osx编程知识来补充我的win32编程经验。我发现GetProcAddress的粗略等价物是dlsym。然后我遇到了一些意想不到的障碍。

在检查旧的论坛帖子和尝试一些东西之后,我的代码得到了工作,但它不符合我的期望,也不符合我在 Apple 自己的文档中看到的内容。我希望这里有人可以帮助我理解为什么。

我所指的苹果文档

该文档提供了一个如下所示的示例:

Person_creator* NewPerson = (Person_creator*)dlsym(lib_handle, "NewPerson");

我通过这样做让我的代码工作:

void (*printSome)() = (void(*)())dlsym(handle, "printSomething");

我认为我的代码应该这样做的地方:

void* printSome = (void*)dlsym(handle, "printSomething");

但是,当我这样做并尝试调用 printSome(( 时,我收到以下错误消息:

Called object type 'void*' is not a function or function pointer

我有一个愚蠢的想法,它可能是空函数指针所独有的东西(尽管我的经验中没有任何迹象表明它会是这样,我只是在试图理解(。

那么,什么给了呢?额外的语法在做什么?为什么这在官方文档中没有出现?

在 C 中,指向函数的指针的声明可能会令人困惑。本网站"cdecl"可以帮助您理解声明的含义。以下是它如何解释工作代码的变量声明:

输入:void (*printSome)()
输出:声明 printSome 作为返回 void 的函数的指针

以下是它如何解码非工作代码的变量声明:

输入:void* printSome
输出:声明 printSome 作为指向 void 的指针

请注意,第一个声明指向函数的指针,而第二个则不声明。因此,您无法对第二个执行函数调用。

使用typedef有助于简化声明,这就是Apple的文档使用typedef Person_creator的原因。下面介绍如何对代码使用typedef

首先,为与要调用的函数具有相同签名的函数写出一个声明:

void PrintFunction(void);

然后把typedef贴在它前面:

typedef void PrintFunction(void);

请注意,我们声明的是函数类型,而不是指向函数的指针类型。

现在,您可以声明指向此类函数的指针,就像声明指向非函数类型的指针一样:

PrintFunction *printSome;

由于dlsym被声明为返回void *,我们必须强制转换它的返回值。我们也可以在那里使用typedef

PrintFunction *printSome = (PrintFunction *)dlsym(handle, "printSomething");

另请注意,C 允许您直接对指向函数的指针执行函数调用,而无需取消引用它。所以你可以这样称呼它:

printSome();

这与显式取消引用它具有相同的效果:

(*printSome)();

更新

既然你提到(在评论中(你正在看手工英雄,我将解释 Casey Muratori 声明函数类型的策略。让我们考虑从第21天开始win32_handmade.cpp:175的这个电话:

Result.UpdateAndRender = (game_update_and_render *)
GetProcAddress(Result.GameCodeDLL, "GameUpdateAndRender");

UpdateAndRender字段在第 157 行声明如下:

game_update_and_render *UpdateAndRender;

所以 Casey 使用相同的typedef game_update_and_render来声明变量并强制转换GetProcAddress的返回值。(我猜 Windows 约定是对变量和函数使用初始大写,对类型使用初始小写,这与 Apple 约定相反(。

typedef game_update_and_renderhandmade.h:190

typedef GAME_UPDATE_AND_RENDER(game_update_and_render);

它使用在前一行声明的GAME_UPDATE_AND_RENDER宏:

#define GAME_UPDATE_AND_RENDER(name) void name(game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer)

最终,game_update_and_render是函数类型的typedef,就像 Apple 示例中的Person_creator或我上面的PrintFunction一样。

为什么凯西使用GAME_UPDATE_AND_RENDER宏?我想这是因为这样他就可以使用相同的宏来定义该类型的实际函数,就像他在下一行(handmade.h:190(所做的那样:

GAME_UPDATE_AND_RENDER(GameUpdateAndRenderStub)
{
}

在这里,他定义了一个名为GameUpdateAndRenderStub的函数,其签名(参数和返回类型(与typedef game_update_and_render相同。如果没有宏,他将不得不重复参数并返回如下类型:

void GameUpdateAndRenderStub(game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer)
{
}