自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

函數(shù)或全局變量重復(fù)定義時(shí)會(huì)怎樣?

開(kāi)發(fā) 前端
如非特殊需求,應(yīng)該盡量避免出現(xiàn)全局變量同名,以免造成意料不到的結(jié)果,例如使用變量時(shí)最小范圍定義,即盡可能避免全局變量,或者使用命名空間(如C++中)。

 [[383839]]

 本文轉(zhuǎn)載自微信公眾號(hào)「編程珠璣」,作者守望先生。轉(zhuǎn)載本文請(qǐng)聯(lián)系編程珠璣(ID:shouwangxiansheng)公眾號(hào)。 

可能有些朋友第一反應(yīng)是,那肯定是編譯不過(guò)嘍:

  1. // 來(lái)源:公眾號(hào)【編程珠璣】 
  2. // 作者:守望先生 
  3. // fun.c 
  4. #include<stdio.h> 
  5. void fun() 
  6.     printf("編程珠璣\n"); 
  7.  
  8. // main.c 
  9. #include<stdio.h> 
  10. void func() 
  11.     printf("公眾號(hào)\n"); 
  12. int main(void) 
  13.     func(); 
  14.     return 0; 

編譯:

  1. $ gcc -o main main.c fun.c 
  2. /tmp/ccKeACRk.o: In function `fun': 
  3. fun.c:(.text+0x0): multiple definition of `fun' 
  4. /tmp/cc4ezgqh.o:main.c:(.text+0x0): first defined here 
  5. collect2: error: ld returned 1 exit status 

可以看到這里報(bào)錯(cuò)了,因?yàn)閒un重復(fù)定義了。

但是重復(fù)定義就會(huì)報(bào)錯(cuò),會(huì)編譯不過(guò)嗎?不全是!

再看下面的代碼:

  1. // 來(lái)源:公眾號(hào)【編程珠璣】 
  2. // 作者:守望先生 
  3. //var.c 
  4. int num; 
  5. void change() 
  6.     num = 1023; 
  7.  
  8. //main.c 
  9. #include<stdio.h> 
  10. void change(); 
  11. int num = 1024; 
  12. int main(void) 
  13.     printf("before:num is %d\n", num); 
  14.     change(); 
  15.     printf("after:num is %d\n", num); 
  16.     return 0; 

輸出結(jié)果:

  1. before:num is 1024  
  2. after:num is 1023  

從結(jié)果中可以看到,雖然num被定義了兩次,但是仍然可以編譯通過(guò),并且正常運(yùn)行。這又是為什么呢?

符號(hào)

在說(shuō)明今天重點(diǎn)分享的內(nèi)容之前,先簡(jiǎn)單了解一下什么是符號(hào)。在《hello程序是如何變成可執(zhí)行文件的》中講到過(guò),ELF文件生成的最后階段會(huì)經(jīng)歷鏈接,而鏈接階段正是基于符號(hào)才能完成。每個(gè)目標(biāo)文件都會(huì)有一個(gè)符號(hào)表。而鏈接過(guò)程正是通過(guò)符號(hào)表中的符號(hào),將不同的目標(biāo)文件“粘”在一起,形成最后的庫(kù)或者可執(zhí)行文件。要查看一個(gè)目標(biāo)文件的符號(hào)信息也很容易:

  1. // symbol.c 
  2. #include<stdio.h> 
  3. int symbol = 1024; 
  4. int func_symbol() 
  5.     return 0; 

編譯:

  1. $ gcc smbol.c #編譯 
  2. $ nm symbol.o #查看符號(hào)信息 
  3. 0000000000000000 T func_symbol 
  4. 0000000000000000 D symbol 

通過(guò)nm命令就可以查看符號(hào)信息,這里就有我們的func_symbol函數(shù)和全局變量symbol的符號(hào)。

關(guān)于nm的使用,在《幾個(gè)命令了解ELF文件的秘密》也有介紹。

除了上面提到的全局符號(hào),目標(biāo)文件中還有其他符號(hào)信息,不過(guò)這不是本文關(guān)注的重點(diǎn)。

強(qiáng)符號(hào)與弱符號(hào)

對(duì)于C/C++語(yǔ)言來(lái)說(shuō),編譯器默認(rèn)函數(shù)和初始化了的全局變量為強(qiáng)符號(hào),未初始化的全局變量為弱符號(hào)。當(dāng)然也可以通過(guò)

  1. __attribute__((weak)) 

來(lái)定義一個(gè)強(qiáng)符號(hào)為弱符號(hào)。

通過(guò)下面的例子來(lái)看看哪些是強(qiáng)符號(hào),哪些是弱符號(hào):

  1. #include<stdio.h> 
  2. int weak; // 未初始化全局變量,弱符號(hào) 
  3. int strong = 1024; // 已初始化全局變量,強(qiáng)符號(hào) 
  4. __attribute__((weak)) int weak1 = 2222; // 使用標(biāo)識(shí)修飾的弱符號(hào) 
  5. int main(void) 
  6.     printf("編程珠璣\n"); 
  7.     return 0; 

注意,這里的強(qiáng)符號(hào)與弱符號(hào)都是針對(duì)定義來(lái)說(shuō)的。

同名時(shí),用哪個(gè)?

對(duì)于多重定義,即標(biāo)題提到的變量重名時(shí),鏈接器有它的處理規(guī)則:

  • 1.強(qiáng)符號(hào)不允許重復(fù)
  • 2.有一個(gè)強(qiáng)符號(hào)和多個(gè)弱符號(hào),使用強(qiáng)符號(hào)
  • 3.多個(gè)弱符號(hào),則隨意選擇一個(gè)

關(guān)于第一點(diǎn),在最開(kāi)始的例子中你已經(jīng)見(jiàn)到了,最常見(jiàn)的情況就是你重復(fù)定義了變量或者函數(shù)等等。

而第二點(diǎn)也有示例,示例中,雖然定義了兩個(gè)num,但是var.c中未初始化的num是弱符號(hào),main.c中的num是強(qiáng)符號(hào),這種情況下編譯正常。只是最終會(huì)使用強(qiáng)符號(hào)的num。

再看一個(gè)第三點(diǎn)的例子也是類(lèi)似,當(dāng)其中main.c的num無(wú)初始化時(shí),也是可以編譯過(guò)的。這種情況下的誤用也就罷了,如果是重復(fù)的符號(hào),但是類(lèi)型不同,問(wèn)題就更大了,即var.c的內(nèi)容如下:

  1. //var.c 
  2. double num; 
  3. void change() 
  4.     num = 1023; 

這里的num變成了double,再次編譯運(yùn)行,你會(huì)發(fā)現(xiàn)意想不到的結(jié)果:

  1. before:num is 1024  
  2. after:num is 0  

為什么修改后是0?原因在于double類(lèi)型的數(shù)據(jù)存儲(chǔ)與int類(lèi)型數(shù)據(jù)存儲(chǔ)格式不一樣(參考《對(duì)浮點(diǎn)數(shù)的一些理解》),且它們占用空間長(zhǎng)度都不一樣,在本文例子中,double占用8字節(jié),而int占用4字節(jié)。

總之,這不是我們想要的結(jié)果,最終的后果可能比我們想象的要嚴(yán)重,要更難發(fā)現(xiàn)。

總結(jié)

如非特殊需求,應(yīng)該盡量避免出現(xiàn)全局變量同名,以免造成意料不到的結(jié)果,例如使用變量時(shí)最小范圍定義,即盡可能避免全局變量,或者使用命名空間(如C++中)。

當(dāng)然了,強(qiáng)弱符號(hào)在某些時(shí)候是非常有用的,例如制作庫(kù)以支持用戶(hù)自定義的庫(kù),這又該怎么做呢?敬請(qǐng)期待下一篇。

參考

參考書(shū)籍

《深入理解計(jì)算機(jī)系統(tǒng)》

《程序員的自我修養(yǎng)》

 

責(zé)任編輯:武曉燕 來(lái)源: 編程珠璣
相關(guān)推薦

2009-12-04 13:14:19

PHP Global變

2013-07-17 16:16:06

Android全局變量定義全局變量Application

2010-11-12 10:08:55

SQL Server全

2013-07-25 15:15:26

iOS開(kāi)發(fā)學(xué)習(xí)iOS全局變量

2013-07-22 14:07:47

2010-03-09 14:12:55

Python全局變量

2009-12-09 17:07:08

PHP unset全局

2010-02-01 14:28:37

Python全局變量

2009-09-24 09:28:00

Scala講座全局變量scala

2009-11-06 13:28:19

Javascript框

2024-05-29 08:49:22

Python全局變量局部變量

2015-01-07 14:41:32

Android全局變量局部變量

2009-12-04 13:31:21

PHP全局變量不能生效

2017-02-08 12:28:37

Android變量總結(jié)

2018-05-14 09:15:24

Python變量函數(shù)

2011-08-23 13:54:10

LUA全局變量

2022-11-28 11:15:20

Tomcat回顯方式

2010-09-08 17:25:17

SQL全局變量

2014-06-23 10:25:12

2023-09-24 23:40:54

Python變量
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)