29

特別なコンパイラ コマンドを使用すると、シンボルをweakとして宣言できます。ウィキペディアによると

弱いシンボルは、オブジェクト ファイルまたは動的ライブラリ内のシンボル定義であり、他のシンボル定義によってオーバーライドされる可能性があります

弱いシンボルが必要なシナリオやアプリケーションは何ですか? 典型的なユースケースは何ですか?

4

5 に答える 5

23

組み込み開発では、たとえば割り込みポインターのベクトルがある場合、関心のない割り込みのデフォルト ハンドラーを取得するために弱いリンクを使用できると非常に便利です。

これは、空のハンドラーを (1 回) 定義し、必要な割り込みポインターごとに 1 つの新しい適切な名前のシンボルを導入することで機能します。これは、デフォルトのハンドラーに弱くリンクされています。

次に、ベクトルはこれらのシンボルで満たされ、同じ(適切な)名前を使用してそのうちの1つを実装することを決定するまで、すべて同じ実際のコードを指し、コードが弱いリンクを「圧倒」し、割り込みテーブルにインストールされるコード。

これは、C とアセンブリが混在して実装されることがよくありますが、C 擬似コードを使用すると、次のようになります。

static void placeholder_isr(void)
{
}

/* Introduce properly-named function pointers, with weak linking.
 * NOTE: This syntax is completely fictional as far as I know.
*/
void (*timer1_isr)() = placeholder_isr __attribute("weak linking");
void (*timer2_isr)() = placeholder_isr __attribute("weak linking");
void (*usart1_isr)() = placeholder_isr __attribute("weak linking");
void (*usart2_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");

/* Declare the table of interrupt handlers. */
static void (*isr_table)[] = {
  timer1_isr,
  timer2_isr,
  usart1_isr,
  usart2_isr,
  dma1_isr,
  dma2_isr,
} __attribute("isr vector"); /* Attribute to place it where it needs to go. */

次に、必要に応じて独自の関数を実装できます。

void timer1_isr(void)
{
  /* Handler ISR from timer1. */
}

他に何も変更する必要はなく、「ただ機能する」だけです。もちろん、あなたの名前が上記の「サポートコード」が期待するものである限り。

于 2013-03-20T14:10:52.680 に答える
22

弱いリンクの用途の 1 つは、C++ 標準で置き換え可能な関数を実装することです。すなわち:

void *operator new(std::size_t);
void *operator new(std::size_t, std::nothrow_t const &) noexcept;
void *operator new[](std::size_t);
void *operator new[](std::size_t, const std::nothrow_t&) noexcept;
void operator delete(void *) noexcept;
void operator delete(void *, std::nothrow_t const &) noexcept;
void operator delete[](void *) noexcept;
void operator delete[](void *, std::nothrow_t const &) noexcept;

これらは実装によって提供される必要がある関数ですが、プログラムがそれらを実装する場合、プログラムの実装は実装のバージョンを置き換えるかオーバーライドします。これは、弱いリンケージを介して簡単に実装できます。

于 2013-03-20T14:37:58.893 に答える
8

典型的かつ日常的な使用例は、インライン関数とテンプレート関数です。

たとえば、次のようにコンパイルした場合のコードのこの部分g++ -shared -fPIC:

extern void a();

inline void foo() { a(); }

void bar() { foo(); }
void baz() { foo(); }

シンボル bar と baz は T (通常) by としてマークされnm、foo は W - weak としてマークされます。

(つまりmn -C ./a.out)

根拠:

インライン関数とテンプレートはヘッダーで定義でき、通常はソースのさまざまな部分で複数回定義され、最終的にアクティブなままになるのは 1 つだけです。

Weak としてマークされていない場合、複数の "foo" シンボルが衝突するか、コンパイラがインライン化を無効にできません。

于 2013-03-20T14:22:26.533 に答える
7

weak属性により、宣言はグローバルではなくウィーク シンボルとして出力されます。これは主に、ユーザー コードでオーバーライドできるライブラリ関数の定義に役立ちますが、関数以外の宣言でも使用できます。ウィーク シンボルは、ELF ターゲットでサポートされています。また、GNU アセンブラーとリンカーを使用する場合は a.out ターゲットでもサポートされています。

弱い属性の例:

弱い.c

extern void foo() __attribute__((weak));

int main() {
if (foo) foo();
} 

foo.c

void foo() {
printf("in foo.\n");
} 

強い.c

extern void foo() ;

int main() {
if (foo) foo();
} 

コンパイル中

$ cc weak.c // Compiles OK
$ cc strong.c // undefined reference to `foo'

「foo」が弱いと宣言されている場合、その定義を省略したり、「リンク時バインディング」を特徴とする別のライブラリに置き換えたりすることができます。リンカーは、未定義の弱いシンボルに対して 0 を埋めます。

于 2013-03-20T14:11:47.253 に答える
5

通常、コードの別の部分で関数定義をオーバーライドできるようにする場合は、弱いリンクを使用します。これは通常、ライブラリを使用する場合にカスタム関数でオーバーライドできるデフォルトのエラー ハンドラなどを指定するライブラリの場合です。

于 2013-03-20T14:19:00.377 に答える