5

假设我们有一个库的以下接口:

// my_interface.h
typedef float (*myfunc)(float);
myfunc get_the_func();

并假设实现如下:

// my_impl.c
#include <math.h>
myfunc get_the_func() {
    return sinf;
}

现在假设客户端代码执行以下操作:

#include "my_interface.h"
...
myfunc func = get_the_func();
printf("%f\n", func(0.0f));

标准是否保证 get_the_function() 将返回标准数学库 sinf() 的地址?如果是这样,标准中的哪个地方暗示了这一点?请注意 sinf() 没有在任何地方显式调用。

4

3 回答 3

6

该标准不要求调用外部函数:被引用足以保存在翻译结果中。根据标准的第 5.1.1.2.1 节,在翻译的八分之一(和最后一个)阶段

所有外部对象和函数引用都已解析。链接库组件以满足对当前翻译中未定义的函数和对象的外部引用。

由于返回指向函数的指针被认为是对其的引用,因此标准保证sinf将链接到您提供给链接器的任何实现,这可能是也可能不是来自标准数学库的实现。

于 2013-05-13T13:47:19.040 回答
3

C 标准不保证get_the_function()将返回sinf数学库中函数的地址。C 标准保证返回的任何指针都是可调用的,就好像您调用了该sinf函数一样,并且它将与指向该函数的任何其他指针进行比较。

在某些架构上,您可能会获得指向编译器特别处理的函数描述符的指针,而且我还看到函数指针值指向动态链接蹦床而不是函数本身。

我记得大约 10 年前关于 gcc 邮件列表的讨论,如果转换为 (void *) 的函数指针实际上需要与转换为 (void *) 的另一个函数指针进行比较,这与 IA64 如何实现函数指针有关(它们可以指向不同库中的不同描述符结构,这意味着要正确实现比较,编译器必须比较描述符的内容而不是指针本身)。但我不记得标准律师决定了什么和/或这是否在链接器中得到解决。

于 2013-05-13T14:12:12.843 回答
0

好的,所以标准中的任何地方都没有说,仅仅因为您 #include <math.h>还将链接到标准数学库……事实上,这不会发生。

您必须链接到 libm 才能使用标准数学库,如下所示:

cc -o foo foo.c -lm 
                ^^^^

标记的选项实际上用于链接器步骤,没有它,无论您是库中的函数作为返回值还是使用它来实际调用函数,都没有链接。

外部符号通过明确指定档案/对象/库或在系统/环境的情况下通过动态链接在运行时延迟链接来解决。

在支持动态链接、弱链接、惰性链接等的环境中,不能保证引用将永远被解析。对于解决方案,必须遍历执行路径。

让我们说它是。尽管如此,您的客户端还需要在链接时或在运行时为支持它的环境提供解决sinf的链接路径。

重点:

客户端用户可以使用任何方式将符号解析为他们认为合适的地址;所以,事实上,没有办法保证你的客户端会链接到标准库并且会解析到系统库sinf。您唯一知道的是,如果执行了该路径;它会导致一个可以使用 sinf 链接查找的地址,或者对于不需要在链接时解析符号的环境,它会毫不客气地崩溃。

更新/澄清:

澄清一下,如果将 sinf 用作变量;那么它需要被解析,但仍然不能保证当客户端代码在他们完成链接步骤时解析符号时,他们将根据数学库解析它。如果我们谈论的是保证。

现在实际上来说,如果客户端链接到标准数学库并且没有提取他们可以的任何覆盖(我在上面指出),那么是的,该符号将被解析为需要与标准库的链接(或者静态或动态)

我最初的回答有点“拘谨”,对此我深表歉意,因为我们谈论的是标准和保证,因此具有讽刺意味。例如,没有什么可以阻止客户端简单地执行以下操作:

富.c:

#include "my_interface.h"
...
myfunc func = get_the_func();
printf("%f\n", func(0.0f));

第一遍编译:

cc -o foo foo.c

得到一个sinf未解决的错误,因此客户端编辑她的源文件:

富.c:

#include "my_interface.h"
...
void * sinef = NULL;
myfunc func = get_the_func();
printf("%f\n", func(0.0f));

现在你有一个完全解决但很好崩溃的程序;

于 2013-05-13T14:07:59.180 回答