-
Notifications
You must be signed in to change notification settings - Fork 0
/
强符号和弱符号.html
269 lines (254 loc) · 18.8 KB
/
强符号和弱符号.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="littlewhite" />
<meta name="copyright" content="littlewhite" />
<meta property="og:type" content="article" />
<meta name="twitter:card" content="summary">
<meta name="keywords" content="c++, Language, " />
<meta property="og:title" content="强符号和弱符号 "/>
<meta property="og:url" content="https://chukeer.github.io/强符号和弱符号.html" />
<meta property="og:description" content="之前在extern “C” 用法详解中已经提到过符号的概念,它是编译器对变量和函数的一种标记,编译器对C和C++代码在生产符号时规则也是不一样的,符号除了本身名字的区别外,还有强符号和弱符号之分 我们先看一段简单的代码 /* test.c */ void hello(); int main() { hello(); return 0; } 很显然,这段代码是没法链接通过的,它会报错undefined reference to hello,说的是hello未定义,因为这里我们只声明了函数hello,而没有定义它。但是我们把代码稍作修改如下 __attribute__((weak)) void hello(); int main() { hello(); return 0; } 这时你会发现,编译链接都可通过,但是运行会报错,因为这时我们将hello声明为了弱符号,在链接时弱符号会被链接器当做0,执行一个地址为0的函数当然会报错,改为如下代码就不会报错了,只是它没有任何输出 __attribute__((weak ..." />
<meta property="og:site_name" content="楚客" />
<meta property="og:article:author" content="littlewhite" />
<meta property="og:article:published_time" content="2014-10-13T00:00:00+08:00" />
<meta property="" content="2014-10-13T00:00:00+08:00" />
<meta name="twitter:title" content="强符号和弱符号 ">
<meta name="twitter:description" content="之前在extern “C” 用法详解中已经提到过符号的概念,它是编译器对变量和函数的一种标记,编译器对C和C++代码在生产符号时规则也是不一样的,符号除了本身名字的区别外,还有强符号和弱符号之分 我们先看一段简单的代码 /* test.c */ void hello(); int main() { hello(); return 0; } 很显然,这段代码是没法链接通过的,它会报错undefined reference to hello,说的是hello未定义,因为这里我们只声明了函数hello,而没有定义它。但是我们把代码稍作修改如下 __attribute__((weak)) void hello(); int main() { hello(); return 0; } 这时你会发现,编译链接都可通过,但是运行会报错,因为这时我们将hello声明为了弱符号,在链接时弱符号会被链接器当做0,执行一个地址为0的函数当然会报错,改为如下代码就不会报错了,只是它没有任何输出 __attribute__((weak ...">
<title>强符号和弱符号 · 楚客
</title>
<!--
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.1/css/font-awesome.css" rel="stylesheet">
--!>
<link href="https://chukeer.github.io/theme/css/bootstrap-combined.min.css" rel="stylesheet">
<link href="https://chukeer.github.io/theme/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="https://chukeer.github.io/theme/css/pygments.css" media="screen">
<link rel="stylesheet" type="text/css" href="https://chukeer.github.io/theme/tipuesearch/tipuesearch.css" media="screen">
<link rel="stylesheet" type="text/css" href="https://chukeer.github.io/theme/css/elegant.css" media="screen">
<link rel="stylesheet" type="text/css" href="https://chukeer.github.io/theme/css/custom.css" media="screen">
</head>
<body>
<div id="content-sans-footer">
<div class="navbar navbar-static-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="https://chukeer.github.io/"><span class=site-name>楚客</span></a>
<div class="nav-collapse collapse">
<ul class="nav pull-right top-menu">
<li ><a href="https://chukeer.github.io">Home</a></li>
<li ><a href="https://chukeer.github.io/categories.html">Categories</a></li>
<li ><a href="https://chukeer.github.io/tags.html">Tags</a></li>
<li ><a href="https://chukeer.github.io/archives.html">Archives</a></li>
<li><form class="navbar-search" action="https://chukeer.github.io/search.html" onsubmit="return validateForm(this.elements['q'].value);"> <input type="text" class="search-query" placeholder="Search" name="q" id="tipue_search_input"></form></li>
</ul>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span1"></div>
<div class="span10">
<article>
<div class="row-fluid">
<header class="page-header span10 offset2">
<h1><a href="https://chukeer.github.io/强符号和弱符号.html"> 强符号和弱符号 </a></h1>
</header>
</div>
<div class="row-fluid">
<div class="span2 table-of-content">
<nav>
<h4>Contents</h4>
<div class="toc">
<ul></ul>
</div>
</nav>
</div>
<div class="span8 article-content">
<p>之前在<a href="http://littlewhite.us/archives/240">extern “C” 用法详解</a>中已经提到过符号的概念,它是编译器对变量和函数的一种标记,编译器对C和C++代码在生产符号时规则也是不一样的,符号除了本身名字的区别外,还有强符号和弱符号之分</p>
<p>我们先看一段简单的代码</p>
<div class="highlight"><pre><span></span><span class="cm">/* test.c */</span><span class="w"></span>
void<span class="w"> </span>hello<span class="o">()</span><span class="err">;</span><span class="w"></span>
int<span class="w"> </span>main<span class="o">()</span><span class="w"></span>
<span class="err">{</span><span class="w"></span>
<span class="w"> </span>hello<span class="o">()</span><span class="err">;</span><span class="w"></span>
<span class="w"> </span><span class="kr">return</span><span class="w"> </span><span class="m">0</span><span class="err">;</span><span class="w"></span>
<span class="err">}</span><span class="w"></span>
</pre></div>
<p>很显然,这段代码是没法链接通过的,它会报错<code>undefined reference to hello</code>,说的是hello未定义,因为这里我们只声明了函数hello,而没有定义它。但是我们把代码稍作修改如下</p>
<div class="highlight"><pre><span></span>__attribute__((weak)) void hello();
int main()
{
hello();
return 0;
}
</pre></div>
<p>这时你会发现,编译链接都可通过,但是运行会报错,因为这时我们将hello声明为了弱符号,在链接时弱符号会被链接器当做0,执行一个地址为0的函数当然会报错,改为如下代码就不会报错了,只是它没有任何输出</p>
<div class="highlight"><pre><span></span>__attribute__((weak)) void hello();
int main()
{
if(hello)
hello();
return 0;
}
</pre></div>
<p>编译器认为,<font color="red"><strong>函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号</strong></font>,链接器在处理强符号和弱符号时有如下规则</p>
<ol>
<li>不同目标文件中,不允许有同名的强符号</li>
<li>如果一个符号在某个目标文件中是强符号,在其它目标文件中为弱符号,选择强符号</li>
<li>如果一个符号在所有目标文件中都是弱符号,选择占用空间最大的,比如目标文件A中有double global_var,文件B中有int global_var,double占用8字节,大于int的4字节,A和B链接后,符号global占8字节</li>
</ol>
<p>对此我们可以简单的验证一下,有如下两个文件</p>
<div class="highlight"><pre><span></span><span class="cm">/* 1.c */</span><span class="w"></span>
char<span class="w"> </span>global_var<span class="err">;</span><span class="w"></span>
int<span class="w"> </span>main<span class="o">()</span><span class="w"></span>
<span class="err">{</span><span class="w"></span>
<span class="w"> </span><span class="kr">return</span><span class="w"> </span><span class="m">0</span><span class="err">;</span><span class="w"></span>
<span class="err">}</span><span class="w"></span>
<span class="cm">/* 2.c */</span><span class="w"></span>
int<span class="w"> </span>global_var<span class="err">;</span><span class="w"></span>
</pre></div>
<p>全局变量global_var在两个文件中都没有初始化,因此都是弱符号,执行编译命令<code>gcc 1.c 2.c</code>,用readelf查看符号表<code>readelf -s a.out</code>,为了查看方便我们只输出最后几行</p>
<div class="highlight"><pre><span></span><span class="n">Num</span><span class="o">:</span> <span class="n">Value</span> <span class="n">Size</span> <span class="n">Type</span> <span class="n">Bind</span> <span class="n">Vis</span> <span class="n">Ndx</span> <span class="n">Name</span>
<span class="mi">62</span><span class="o">:</span> <span class="mi">0000000000600818</span> <span class="mi">4</span> <span class="n">OBJECT</span> <span class="n">GLOBAL</span> <span class="n">DEFAULT</span> <span class="mi">25</span> <span class="n">global_var</span>
<span class="mi">63</span><span class="o">:</span> <span class="mi">0000000000400474</span> <span class="mi">11</span> <span class="n">FUNC</span> <span class="n">GLOBAL</span> <span class="n">DEFAULT</span> <span class="mi">13</span> <span class="n">main</span>
<span class="mi">64</span><span class="o">:</span> <span class="mi">0000000000400358</span> <span class="mi">0</span> <span class="n">FUNC</span> <span class="n">GLOBAL</span> <span class="n">DEFAULT</span> <span class="mi">11</span> <span class="n">_init</span>
</pre></div>
<p>这里符号global_var占用的size是4,说明链接器选择的是占用空间更大的int global_var,我们再稍作修改,将1.c中的全局变量初始化,如下</p>
<div class="highlight"><pre><span></span><span class="cm">/* 1.c */</span><span class="w"></span>
char<span class="w"> </span>global_var<span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="err">;</span><span class="w"></span>
int<span class="w"> </span>main<span class="o">()</span><span class="w"></span>
<span class="err">{</span><span class="w"></span>
<span class="w"> </span><span class="kr">return</span><span class="w"> </span><span class="m">0</span><span class="err">;</span><span class="w"></span>
<span class="err">}</span><span class="w"></span>
<span class="cm">/* 2.c */</span><span class="w"></span>
int<span class="w"> </span>global_var<span class="err">;</span><span class="w"></span>
</pre></div>
<p>这时1.c中的global_var为强符号,2.c中的global_var为弱符号,同样编译之后用readelf查看符号表<code>readelf -s a.out</code>如下</p>
<div class="highlight"><pre><span></span><span class="n">Num</span><span class="o">:</span> <span class="n">Value</span> <span class="n">Size</span> <span class="n">Type</span> <span class="n">Bind</span> <span class="n">Vis</span> <span class="n">Ndx</span> <span class="n">Name</span>
<span class="mi">62</span><span class="o">:</span> <span class="mi">0000000000600818</span> <span class="mi">1</span> <span class="n">OBJECT</span> <span class="n">GLOBAL</span> <span class="n">DEFAULT</span> <span class="mi">25</span> <span class="n">global_var</span>
<span class="mi">63</span><span class="o">:</span> <span class="mi">0000000000400474</span> <span class="mi">11</span> <span class="n">FUNC</span> <span class="n">GLOBAL</span> <span class="n">DEFAULT</span> <span class="mi">13</span> <span class="n">main</span>
<span class="mi">64</span><span class="o">:</span> <span class="mi">0000000000400358</span> <span class="mi">0</span> <span class="n">FUNC</span> <span class="n">GLOBAL</span> <span class="n">DEFAULT</span> <span class="mi">11</span> <span class="n">_init</span>
</pre></div>
<p>此时符号global_var占用的size是1,说明链接器选择的是强符号</p>
<p>在写代码时应该尽量避免有不同类型的符号,否则会引发非常诡异且不易察觉的错误,为了避免可以采取如下措施:</p>
<ul>
<li>上策:消除所有的全局变量 </li>
<li>中策:将全局变量声明为static类型,并提供接口供访问 </li>
<li>下策:全局变量一定要初始化,哪怕初始化为0 </li>
<li>必备:打开gcc的-fno-common选项,它会禁止有不同类型的符号</li>
</ul>
<p>说了这么多,好像在说应该尽量用强符号,那弱符号有什么用呢,所谓存在即合理,有时候我们甚至需要显示定义弱符号,这对库函数会非常有用,比如库中的弱符号可以被用户自定义的强符号覆盖,从而实现自定义的库版本,或者在使用某些扩展功能时,用户可以定义一个弱符号,当链接了该功能时,功能模块可以正常使用,如果去掉功能模块,程序也可正常链接,只是缺少某些功能而已,比如我们可以通过下面的代码判断程序是否链接了pthread库,从而决定执行什么样的操作</p>
<div class="highlight"><pre><span></span><span class="cm">/* test.c */</span>
<span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf"><pthread.h></span><span class="cp"></span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">weak</span><span class="p">))</span> <span class="kt">int</span> <span class="n">pthread_create</span><span class="p">(</span>
<span class="n">pthread_t</span><span class="o">*</span><span class="p">,</span>
<span class="k">const</span> <span class="n">pthread_attr_t</span><span class="o">*</span><span class="p">,</span>
<span class="kt">void</span><span class="o">*</span><span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="kt">void</span><span class="o">*</span><span class="p">),</span>
<span class="kt">void</span><span class="o">*</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">pthread_create</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"This is multi-thread version!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"This is single-thread version!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>编译运行结果如下</p>
<div class="highlight"><pre><span></span>$ gcc test.c
$ ./a.out
This is single-thread version!
$ gcc test.c -lpthread
$ a.out
This is multi-thread version!
</pre></div>
<p>EOF</p>
<hr/>
<p>本文参考:</p>
<ol>
<li>《程序员的自我修养》3.5.5章节</li>
<li><a href="http://blog.csdn.net/astrotycoon/article/details/8008629">http://blog.csdn.net/astrotycoon/article/details/8008629</a></li>
</ol>
<hr/>
<aside>
<nav>
<ul class="articles-timeline">
<li class="previous-article">« <a href="https://chukeer.github.io/Mac必备软件推荐.html" title="Previous: Mac必备软件推荐">Mac必备软件推荐</a></li>
<li class="next-article"><a href="https://chukeer.github.io/没有main函数的helloworld.html" title="Next: 没有main函数的helloworld">没有main函数的helloworld</a> »</li>
</ul>
</nav>
</aside>
</div>
<section>
<div class="span2" style="float:right;font-size:0.9em;">
<table class="table">
<!-- <time pubdate="pubdate" datetime="2014-10-13T00:00:00+08:00">10 13, 2014</time> -->
<tr>
<td>Published</td>
<td><time pubdate="pubdate" datetime="2014-10-13T00:00:00+08:00">2014-10-13</time></td>
</tr>
<tr>
<td>Category</td>
<td><a class="category-link" href="https://chukeer.github.io/categories.html#language-ref">Language</a></td>
</tr>
<tr>
<td>Tags</td>
<td>
<ul class="list-of-tags tags-in-article">
<li><a href="https://chukeer.github.io/tags.html#c-ref">c++
<span>15</span>
</a></li>
</ul>
</td>
</tr>
</table>
</div>
</section>
</div>
</article>
</div>
<div class="span1"></div>
</div>
</div>
<div id="push"></div>
</div>
<footer>
<div id="footer">
<ul class="footer-content">
<li class="elegant-power">Powered by <a href="http://getpelican.com/" title="Pelican Home Page">Pelican</a>. Theme: <a href="http://oncrashreboot.com/pelican-elegant" title="Theme Elegant Home Page">Elegant</a> by <a href="http://oncrashreboot.com" title="Talha Mansoor Home Page">Talha Mansoor</a></li>
</ul>
</div>
</footer> <!--
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
-->
<script src="https://chukeer.github.io/theme/js/jquery.min.js"></script>
<script src="https://chukeer.github.io/theme/js/bootstrap.min.js"></script>
<script>
function validateForm(query)
{
return (query.length > 0);
}
</script>
<script>
$("div.article-content table").addClass("table table-hover");
</script>
</body>
<!-- Theme: Elegant built for Pelican
License : http://oncrashreboot.com/pelican-elegant -->
</html>