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

main.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. function が ANSI C 関数であるか、(ここのように) POSIX 関数であるかに違いはありますか?

  4. オーバーライド関数はどこで呼び出されますか? .o少なくとも、同じリンク内の他のファイルによって、.aリンクコマンドに追加されたファイルも推定されます。-lリンカーのコマンドラインオプションで追加された静的または動的ライブラリはどうですか?

  5. 可能であれば、オーバーライドされた getline から getline のライブラリ バージョンを呼び出すにはどうすればよいですか?

4

4 に答える 4

17

リンカーは、ライブラリを検索する前に、最初にコマンド ラインで指定したファイルのシンボルを検索します。getlineこれは、が定義されていることを確認するとすぐに、別のgetlineシンボルを検索しないことを意味します。これは、リンカーがすべてのプラットフォームでどのように機能するかです。

getlineこれはもちろん、関数リンカーの観点からオリジナルであるため、「オリジナル」を呼び出す可能性がないという点で、5番目のポイントに影響を与えます。

5番目のポイントについては、たとえばこの古い回答を見たいと思うかもしれません。

于 2013-09-26T08:31:28.840 に答える
4

プログラムで同じ名前の関数を 2 つ持つ標準的な方法はありませんが、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 に答える