12

我正在使用dlsym()in并且我有一个问题,是否应该显式转换C的返回值或者是否正确地隐式转换。dlsym()这是功能:

double (*(compile)(void))(double x, double y)
{
    if (system("scan-build clang -fPIC -shared -g -Wall -Werror -pedantic "
           "-std=c11 -O0 -lm foo.c -o foo.so") != 0) {
        exit(EXIT_FAILURE);
    }

    void *handle;
    handle = dlopen("./foo.so", RTLD_LAZY);
    if (!handle) {
        printf("Failed to load foo.so: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    foo f;
    f = (foo)dlsym(handle, "foo");
    if (!f) {
        printf("Failed to find symbol foo in foo.so: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }
    return f;
}

该函数compile()不接受值并返回一个指向函数的指针,该函数将两个doubles 作为输入并返回一个双精度值。然后我设置了一个编译共享对象的系统调用foo.so。然后我打开foo.so. dlopen()然后dlsym()找到foofoo.so返回一个类型的对象foo,我在标题中定义为:

typedef double (*foo)(double, double);

我必须投dlsym()吗?

4

2 回答 2

17

编写 C 标准是为了假定指向不同对象类型的指针,尤其是指向与对象类型相反的函数的指针,可能具有不同的表示形式。这就是为什么,一般来说,你不想混合指针,或者如果你这样做,现代编译器会警告你,如果你想消除警告,你通常会使用显式强制转换。

dlsym另一方面,它的存在假设所有指针都几乎相同,因为它应该能够为您返回指向目标文件中任何数据类型(对象函数)的任何指针。

换句话说,使用dlsym的代码本质上是不可移植的,从某种意义上说,它不是广泛可移植的,从某种意义上说,它“仅”可移植到所有指针都可以安全地相互转换的那些机器上。(这当然是当今几乎所有流行的机器。)

所以,是的,您需要强制转换指针以使警告静音,这样做可能会使您的代码在所有指针都不相同的机器上的可移植性降低(并且警告,如果未静音,将正确通知您您的代码将无法工作),但dlsym无论如何都不会在这些机器上工作(甚至以当前形式存在)。

(如果gcc -pedantic甚至警告您关于从函数指针类型的显式转换,除了切换到不同版本的,或者当然不使用void *之外,您无能为力。)gcc-pedantic


附录:我的回答听起来像是在指向不同类型数据的指针之间转换可能是一个问题,但这通常没问题。类型void *被很好地定义为通用数据指针:它是malloc返回的,它被定义为可以悄悄地转换为任何对象指针类型——也就是说,你甚至不需要强制转换。所以它几乎是返回类型的一个不错的选择dlsym除了函数指针的小问题。 malloc从来没有这个问题(你几乎不会尝试 malloc 指向函数的指针),而dlsym总是有这个问题(你通常试图在动态加载的目标文件中访问的符号是代码至少和它们一样频繁)重新数据)。但是函数指针是什么void *不能保证转换为,因此您很可能会收到警告,这就是您需要演员表的原因,-pedantic即使使用演员表,您也可能会收到警告。

于 2015-07-20T23:16:31.153 回答
12

dlsym()返回一个void*值。这个指针值可以引用一个对象或一个函数。

如果它指向一个对象,则不需要强制转换,因为 C 定义了从void*任何指针到对象类型的隐式转换:

int *ptr = dlsym(handle, "name");

如果它指向一个函数(这可能更常见),则不会隐式转换void*为任何指向函数的指针类型,因此需要进行强制转换。

在标准 C 中,不能保证void*值可以有意义地转换为函数指针。定义 的 POSIXdlsym()隐式保证 的void*返回值dlsym() 可以有意义地转换为指向函数的指针类型——只要目标是对应函数的正确类型。

假设我们正在处理一个没有参数的 void 函数:

typedef void (*func_ptr_t)(void);
func_ptr_t fptr = (func_ptr_t)dlsym(handle, "name");

碰巧的是, gcc (with -pedantic) 对此发出警告:

warning: ISO C forbids conversion of object pointer to function pointer type

这个警告并不完全正确。ISO C 实际上并不禁止将对象指针转换为函数指针类型。该标准列出了一些强制转换运算符可能不会违反的约束;转换void*为函数指针类型不是其中之一。C 标准没有定义这种转换的行为,但由于 POSIX 在这种特殊情况下确实没有问题。

对史蒂夫·萨米特的回答的评论表明:

*(void **) (&f) = dlsym(handle, "foo");

将使 gcc 的警告静音。它会,但它做出了 C 或 POSIX 都不能保证的假设。POSIX 保证 的结果dlsym可以转换为函数指针;它不保证它具有相同的表示或对齐方式。它可能会起作用,但我不推荐它。

于 2015-07-21T00:55:01.440 回答