我尝试在代码中使用dlopen()
和dlsym()
,并用gcc
编译它。
这里是第一个文件。
/* main.c */
#include <dlfcn.h>
int main()
{
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void (*func)() = dlsym(handle, "func");
func();
}
return 0;
}
这是第二个文件。
/* foo.c */
#include <stdio.h>
void func()
{
printf("hello, world\n");
}
下面是我编译和运行代码的方法。
$ gcc -std=c99 -pedantic -Wall -Wextra -shared -fPIC -o foo.so foo.c
$ gcc -std=c99 -pedantic -Wall -Wextra -ldl -o main main.c
main.c: In function ‘main’:
main.c:10:26: warning: ISO C forbids initialization between function pointer and ‘void *’ [-Wpedantic]
void (*func)() = dlsym(handle, "func");
^
$ ./main
hello, world
我怎样才能摆脱警告?
类型转换没有帮助。如果我试图将dlsym()
的返回值类型转换为函数指针,我会得到这个警告。
main.c:10:26: warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]
void (*func)() = (void (*)()) dlsym(handle, "func");
^
怎样才能让编译器相信这段代码是正确的呢?
7条答案
按热度按时间vcudknz31#
如果你想让自己的方法更加精确,不要试图解析函数的地址,而是从动态库中导出某种结构:
在图书馆
在调用方
这种技术实际上非常常见,不是为了可移植性-- POSIX保证函数指针可以与void* 相互转换--而是因为它允许更大的灵活性。
cclgggtu2#
这使得我的代码变得十分迂腐:
(我在这里找到的http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html)
20jt8wwn3#
这里的问题是指向对象的指针与函数指针被巧妙地分开了。在ISO/IEC 9899:201 xpaper**§6.3.2.3指针中指出:
1.指向void的指针可以转换为指向任何对象类型的指针,也可以从指向任何对象类型的指针转换为指向void的指针,然后再转换回来;结果将与原始指针比较相等。
。
1.指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再转换回来;其结果应该与原始指针相等。如果转换后的指针用于调用一个类型与所指向的类型不兼容的函数,则该行为未定义。
因此,函数指针不同于对象指针,因此
void *
对函数指针的赋值总是非严格兼容**。无论如何,正如我在评论中所说的,在99.9999....9999%的情况下,它是允许的,这要归功于附录J -可移植性问题,§J.5.7函数指针转换,前面提到的论文指出:
1.指向对象或void的指针可以转换为指向函数的指针,从而允许将数据作为函数调用(6.5.4)。
1.指向函数的指针可以转换为指向对象或void的指针,从而允许检查或修改函数(例如,通过调试器)(6.5.4)。
现在从实用的Angular 来看,一种避免将代码分割成更多文件的技术是使用pragma来抑制一小段代码的学究式警告。
更残酷的形式可以是:
一个更复杂的方法可以是在一个小函数中隔离出违规代码,然后强制其内联。这与其说是一个有效的解决方案,不如说是一个补充,但会抑制不必要的诊断:
s2j5cfk04#
若要保留代码的
-pedantic
选项,同时保留不严格符合的代码部分,请将该代码分隔到具有自定义警告选项的单独文件中。因此,创建一个函数, Package
dlsym
函数并返回一个函数指针。将其放在一个单独的文件中,并在不使用-pedantic
的情况下编译该文件。chhkpiq45#
您可以使用
union
,如下所示:mnemlml86#
POSIX标准explicitly states,你可以“运行”一个地址作为一个指针传递给对象(强调我):
请注意,从
void *
指针到函数指针的转换如下所示:ISO C标准未定义。此标准要求此转换在一致的实现上正确工作。
要执行此操作,您需要首先“激活”对象指针。
然后可以直接调用对象变量,
也可以将其转换为另一个适当函数类型变量。
加法
尽管从对象指针到函数指针的转换可能会有问题,但在每个标准C中都允许在它们之间转换不同类型的函数指针。您可以利用此权限并使用
void (*) (void)
作为宏的固定目标类型。因此,想象一个
addition.c
库,以下是同一代码的四个变体。
第一名
第二名
第三名
第四名
ruoxqz4g7#
编译器只“试图提供帮助”,因此必须使用两种类型转换: