今天读别人代码时看到一个“#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;