今天讀別人代碼時看到一個“#pragma weak”,一時沒明白,上網研究了一個下午終於稍微了解了一點C、C++中的“弱符號”,下面是我的理解,不正確的地方望大家指正。
本文主要從下面三個方面講“弱符號”:
1. 什么是“弱符號”? 它與“強符號”的區別是什么?
2. 弱符號的有什么作用?
3. 弱符號的實際應用實例
1.什么是弱符號?
在WikiPedia中弱符號的定義是:
a weak symbol is a symbol definition in an object file or dynamic library that may be overridden by other symbol definitions
A weak symbol denotes a specially annotated symbol during linking of Executable and Linkable Format (ELF) object files.
By default, without any annotation, a symbol in an object file is strong.
During linking, a strong symbol can override a weak symbol of the same name.
In contrast, two strong symbols that share a name yield a link error during link-time.
When linking a binary executable, a weakly declared symbol does not need a definition.
In comparison, (by default) a declared strong symbol without a definition triggers an undefined symbol link error. Weak symbols are not mentioned by C or C++ language standards; as such, inserting them into code is not very portable.
Even if two platforms support the same or similar syntax for marking symbols as weak,
the semantics may differ in subtle points, e.g. whether weak symbols during dynamic linking at runtime lose their semantics or not
從第一個定義可以知道,“弱符號“是在一個文件或者動態庫中定義的,可以被其他地方定義的符號overridden的符號。這里重點是“overridden”
從第二個定義,我們可以知道“弱符號”跟強符號主要有如下區別:
1. 弱符號可以只有申明,沒有定義,強符號必須有定義
2. 弱符號可以定義多次,強符號只能定義。
另外第二個定義中還提到,弱符號並不是C、C++規范中的內容,這個跟編譯器相關,不可移植。
在中文的很多文章中把weak symbol分為了兩種“弱符號”(變量)“弱引用”(函數),但WikiPedia中弱符號的例子都是用的函數。
2.弱符號的作用
我的理解“弱符號變量“是C中遺留下來的,它除了引起麻煩,沒有什么作用。C語言中凡是沒有初始化的全局變量都是弱符號變量,如果存在多個同名的弱符號變量,編譯器在鏈接時可以任意選一個(有的編譯器選擇占用空間最大的那個定義)。這樣,你的代碼中定義了一個x,忘了初始化,而你用到的某個庫中又剛好定義了一個未初始化的x,后果大家可以自己想。C++中不會出現這個問題,因為C++中所有未初始化的全局變量都初始化為0。
弱符號函數的主要作用是為了多態,即使用定義中的”overridden“,一般在庫中使用得比較多,比如你在你的庫中為某個函數提供一個默認實現,用戶如果想定制化的話可以自己實現一個。
例子:在library_foo.h中什么函數foo為弱符號函數,library_foo.cc中為foo提供一個默認實現,在my_foo.cc中為foo提供一個定制化的實現。
注:定義一個函數為弱函數有兩種方式
1. 使用“#pragma week function”
2. 函數后面加“ __attribute__((weak))”,
我用的gcc好像不支持第一種方式。
//library_foo.h #ifndef __LIBRARY_FOO_H__ #define __LIBRARY_FOO_H__ void foo() __attribute__((weak)); void f(); #endif
//library_foo.cc #include <iostream> #include "library_foo.h" void foo(){ std::cout<<"default foo"<<std::endl; } void f(){ foo(); }
//main.cc #include <iostream> #include "library_foo.h" using namespace std; int main(int argc, char* argv[]){ f(); }
//my_foo.cc #include <iostream> void foo(){ std::cout<<"My customized foo"<<std::endl; }
編譯及運行結果如下:
> g++ library_foo.cc main.cc my_foo.cc -o customized.x > g++ library_foo.cc main.cc -o not_customized.x > ./customized.x my customized foo > ./not_customized.x default foo
可以看到,當鏈接my_foo.cc是,使用的是定制的foo.
3. 弱符號的實際應用實例
在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;