日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

函数或全局变量重复定义时会怎样?

發布時間:2023/12/2 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 函数或全局变量重复定义时会怎样? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

可能有些朋友第一反應是,那肯定是編譯不過嘍:

//?fun.c #include void?func() {printf("編程珠璣\n"); }//?main.c #include void?func() {printf("公眾號\n"); } int?main(void) {func();return?0; }

編譯:

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

可以看到這里報錯了,因為fun重復定義了。

但是重復定義就會報錯,會編譯不過嗎?不全是!

再看下面的代碼:

//var.c int?num; void?change() {num?=?1023; }//main.c #include void?change(); int?num?=?1024; int?main(void) {printf("before:num?is?%d\n",?num);change();printf("after:num?is?%d\n",?num);return?0; }

輸出結果:

before:num?is?1024? after:num?is?1023?

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

符號

在說明今天重點分享的內容之前,先簡單了解一下什么是符號。ELF文件生成的最后階段會經歷鏈接,而鏈接階段正是基于符號才能完成。每個目標文件都會有一個符號表。而鏈接過程正是通過符號表中的符號,將不同的目標文件“粘”在一起,形成最后的庫或者可執行文件。要查看一個目標文件的符號信息也很容易:

//?symbol.c #include int?symbol?=?1024; int?func_symbol() {return?0; }

編譯:

$?gcc?smbol.c?#編譯 $?nm?symbol.o?#查看符號信息 0000000000000000?T?func_symbol 0000000000000000?D?symbol

通過nm命令就可以查看符號信息,這里就有我們的func_symbol函數和全局變量symbol的符號。

除了上面提到的全局符號,目標文件中還有其他符號信息,不過這不是本文關注的重點。

強符號與弱符號

對于C/C 語言來說,編譯器默認函數和初始化了的全局變量為強符號,未初始化的全局變量為弱符號。當然也可以通過

__attribute__((weak))

來定義一個強符號為弱符號。

通過下面的例子來看看哪些是強符號,哪些是弱符號:

#include int?weak;?//?未初始化全局變量,弱符號 int?strong?=?1024;?//?已初始化全局變量,強符號 __attribute__((weak))?int?weak1?=?2222;?//?使用標識修飾的弱符號 int?main(void) {printf("編程珠璣\n");return?0; }

注意,這里的強符號與弱符號都是針對定義來說的。

同名時,用哪個?

對于多重定義,即標題提到的變量重名時,鏈接器有它的處理規則:

  • 1.強符號不允許重復

  • 2.有一個強符號和多個弱符號,使用強符號

  • 3.多個弱符號,則隨意選擇一個

關于第一點,在最開始的例子中你已經見到了,最常見的情況就是你重復定義了變量或者函數等等。

而第二點也有示例,示例中,雖然定義了兩個num,但是var.c中未初始化的num是弱符號,main.c中的num是強符號,這種情況下編譯正常。只是最終會使用強符號的num。

再看一個第三點的例子也是類似,當其中main.c的num無初始化時,也是可以編譯過的。這種情況下的誤用也就罷了,如果是重復的符號,但是類型不同,問題就更大了,即var.c的內容如下:

//var.c double?num; void?change() {num?=?1023; }

這里的num變成了double,再次編譯運行,你會發現意想不到的結果:

before:num?is?1024? after:num?is?0?

為什么修改后是0?原因在于double類型的數據存儲與int類型數據存儲格式不一樣,且它們占用空間長度都不一樣,在本文例子中,double占用8字節,而int占用4字節。

總之,這不是我們想要的結果,最終的后果可能比我們想象的要嚴重,要更難發現。

總結

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

聲明:

本文于網絡整理,版權歸原作者所有,如來源信息有誤或侵犯權益,請聯系我們刪除或授權事宜。

總結

以上是生活随笔為你收集整理的函数或全局变量重复定义时会怎样?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。