15

我对这段代码如何以及为什么会这样工作感到有些困惑。在我从事的任何项目中,我实际上都没有遇到过这种情况,我什至没有想过自己做。

override_getline.c:

#include <stdio.h>

#define OVERRIDE_GETLINE

#ifdef OVERRIDE_GETLINE
ssize_t getline(char **lineptr, size_t *n, FILE *stream)
{
    printf("getline &lineptr=%p &n=%p &stream=%p\n", lineptr, n, stream);
    return -1; // note: errno has undefined value
}
#endif

主.c:

#include <stdio.h>

int main()
{
    char *buf = NULL;
    size_t len = 0;
    printf("Hello World! %zd\n", getline(&buf, &len, stdin));
    return 0;
}

最后,示例编译并运行命令:

gcc main.c override_getline.c && ./a.out

使用OVERRIDE_GETLINE定义,自定义函数被调用,如果它被注释掉,正常的库函数被调用,并且都按预期工作。

问题

  1. 什么是正确的术语?“覆盖”,“阴影”,还有别的吗?

  2. 这是特定于 gcc 的,还是 POSIX,或 ANSI C,甚至完全未定义?

  3. 如果函数是 ANSI C 函数或(如这里)POSIX 函数,它有什么区别吗?

  4. 覆盖函数在哪里被调用?.o至少通过同一链接中的其他文件,我认为.a文件也添加到链接命令中。-l使用链接器的命令行选项添加的静态或动态库怎么样?

  5. 如果可能,如何从覆盖的 getline 调用库版本的 getline?

4

4 回答 4

17

链接器将首先搜索您在命令行中提供的文件中的符号,然后再搜索库。这意味着一旦它看到getline已定义,它将不再寻找另一个getline符号。这就是链接器在所有平台上的工作方式。

这当然对您的第五点有影响,因为从链接器的角度来看,getline您的函数原始的,因此不可能调用 "original" 。

对于第五点,您可能想看看这个旧答案

于 2013-09-26T08:31:28.840 回答
4

在你的程序中没有标准的方法来拥有两个同名的函数,但是通过一些类似 UNIX 的实现(特别是 GNU libc)你可能能够摆脱这个:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>

ssize_t getline(char **lineptr, size_t *n, FILE *stream)
{
   ssize_t (*realfunc)(char**, size_t *, FILE*) =
       (ssize_t(*)(char**, size_t *, FILE*))(dlsym (RTLD_NEXT, "getline"));
   return realfunc(lineptr, n, stream);
}

您需要为此链接-ldl

于 2013-09-26T08:42:52.153 回答
1

这里发生的是您依赖链接器的行为。getline链接器在看到标准库中的版本之前找到您的实现,因此它链接到您的例程。因此,实际上您是通过链接顺序机制覆盖该功能。当然,其他链接器的行为可能会有所不同,我相信如果您指定适当的命令行开关,gcc 链接器甚至可能会抱怨重复符号。

为了能够调用您的自定义例程和库例程,您通常会求助于宏,例如

#ifdef OVERRIDE_GETLINE
#define GETLINE(l, n, s) my_getline(l, n, s)
#else
#define GETLINE(l, n, s) getline(l, n, s)
#endif

#ifdef OVERRIDE_GETLINE
ssize_t my_getline(char **lineptr, size_t *n, FILE *stream)
{
   // ...
   return getline(lineptr, n, stream);
}
#endif

请注意,这需要您的代码调用getlineas GETLINE,这相当难看。

于 2013-09-26T08:31:37.620 回答
0

如果您与共享库链接,您会看到预期的行为。链接器只会将它分配给您的功能,就像它最初一样。它也将从任何其他外部库函数中正确调用,因为链接器会在扫描链接库时使您的函数可导出。

但是 - 如果你没有链接到你的函数的外部库(所以它没有标记为可导出,也没有插入到符号表中),然后 dlopen() 一些想要在运行时使用它的库 -它不会找到所需的功能。此外,如果您首先 dlopen(RTLD_NOW|RTLD_GLOBAL) 原始库,则每个后续 dlopen() 的库都将使用库代码,而不是您的。无论如何,您的代码(或您在编译阶段而不是运行时链接的任何库)仍将与您的函数保持一致。

于 2013-09-26T09:10:52.010 回答