*(void **) (&funcp) 在这行涉及 dlsym() 的代码中做了什么?

What does *(void **) (&funcp) do in this line of code involving dlsym()?

谁能帮我解释这行代码(来自here)?

*(void **) (&funcp) = dlsym(libHandle, argv[2]); 

我不明白 *(void **) (&funcp) 的作用。

dlsym() 与 dlopen() 和 dlclose() 有关,请参阅手册页。

你看到的那一行是在 dlopen() 加载的二进制对象中查找一个符号(获取符号的值,在本例中假定它是一个函数地址),并赋值它指向指针变量funcp

funcp 被类型转换为相应的函数类型时,意味着给定适当的函数签名,该函数可以通过 funcp 调用并传递参数。

dl(动态库)函数集是通常在支持 dlopen/dlsym/dlclose 的系统上促进插件的机制。插件必须符合用户或社区定义的接口,想要加载插件的代码也必须知道该接口,以便知道如何按名称查找和定义符号,以及如何转换和使用它们.

另一种说法是,它允许您调用一个在 link 时间不可用的对象,并让您的代码在运行时执行 linker 为您处理的事情在 link 时间。

dlsym() returns a (void *) 因为它不能对它正在加载的符号做任何假设,它可以是一个变量,一个函数入口点等等...来做任何事情返回的值,一般需要将其转换为与 dlopen() 加载的二进制文件中的符号对应的类型。

这可能会澄清(来自 here):

(更新: 我没有包含完全相同的 link 来刻薄。我错过了 link原始问题。 :P)

/* The rather clumsy cast above is necessary because the ISO C standard
   does not require that pointers to functions can be cast back and
   forth to 'void *'. (See TLPI pages 863-864.) SUSv3 TC1 and SUSv4
   accept the ISO C requirement and propose casts of the above
   form as the workaround. However, the 2013 Technical Corrigendum
   (TC1) to SUSv4 requires casts of the following more natural form
   to work correctly:

       funcp = (void (*)()) dlsym(libHandle, argv[2]);

   Various current compilers (e.g., gcc with the '-pedantic' flag)
   may still complain about such casts, however. */

通过将 funcp 中的数据重新解释为 void* 和存储到那个。这是通过获取 funcp 的地址(变量本身的地址),假装该地址引用 void* (通过 (void**) 转换),取消引用它,并存储void*dlsym() 到它。更简单的形式也可能在实践中起作用。

这种 "reinterpreting" 数据的方法通过获取其地址,将该地址转换为指向不同类型的指针并取消引用,通常被称为 type punning方法。 双关语 来自相同的数据,当以不同的方式解释时具有不同的含义,这也是真正的双关语的工作方式。

(在某些情况下,类型双关可能是不安全的,包括当编译器使用 严格的别名规则 时,它假设某些不同类型的指针不 alias(它们不引用相同的数据)。上面的转换可能会违反严格的别名,因为你得到一个函数指针和一个引用相同数据的 void*,尽管它是"likely to work in practice" 在这种情况下。)

(ISO C 不要求函数指针可以安全地转换为 void 指针并返回的原因可能是函数和数据(void 指针引用 "data")分别存储在某些机器上。由于它们是分开的,它们也可能使用不同的地址长度或格式,因此函数指针和数据指针之间的转换可能没有意义。以这种方式分离代码和数据的体系结构称为 哈佛架构.)