網站首頁 編程語言 正文
1 什么是鏈接器
典型的鏈接器把由編譯器或匯編器生成的若干個目標模塊,整合成一個被稱為載入模塊或可執行文件的實體–該實體能夠被操作系統直接執行。
鏈接器通常把目標模塊看成是由一組外部對象組成的。每個外部對象代表著機器內存中的某個部分,并通過一個外部名稱來識別。因此,==程序中的每個函數和每個外部變量,如果沒有被聲明為static,就都是一個外部對象。==某些C編譯器會對靜態函數和靜態變量的名稱做一定改變,將它們也作為外部對象。由于經過了“名稱修飾”,因此它們不會與其它源程序文件中的同名函數或同名變量發生命名沖突。
2 聲明與定義
extern int a;
上面的這段代碼并不是對a的定義,而是說明a是一個外部整型變量。
注意:引入之后,假如引入的位置在函數之外,就相當于在那個位置定義了全局變量,同樣遵循局部變量優先原則,如果引入位置在某個函數之內,就相當于是一個局部變量,作用域與那個地方定義的局部變量相類似,此處討論聲明周期沒有任何意義。
int a; extern int a;
上面的這兩條語句既可以是在同一個源文件中,也可以位于程序的不同源文件之中。
==注意:每個外部變量只能定義一次。==如果外部變量的多個定義各指定一個初始值,例如:
int a = 7;
出現在一個源文件中,而
int a = 9;
出現在另一個源文件中,大多數系統都會拒絕接收該程序。但是,如果一個外部變量在多個源文件中定義卻沒有指定初始值,那么**某些系統會接受這個程序,而另外一些系統則不會接受。**所以,每個外部變量必須只定義一次。
3 命名沖突
3.1 命名沖突
如果在兩個不同的源文件中都包括了定義
int a;
那么它要么表示程序錯誤(如果鏈接器進制外部變量重復命名的話),要么在兩個源文件中共享a的同一個實例(無論兩個源文件中的外部變量是否應該被共享)。即使其中a的一個定義是出現在系統提供的庫文件中,也仍然進行同樣的處理。
3.2 static修飾符
static int a;
static修飾a之后,a的作用域將被限制在一個源文件中,對于其它源文件,a是不可見的,且無法再被extern所引用,當然,static也適用于函數。使用static之后,我們就可以在其它的源文件中定義和這個已經被static修飾后的同名的變量或者函數。
4 形參、實參、返回值
如果我們使用的函數并未進行聲明,但是已經在后面進行了定義,此時會默認函數返回類型為int型,這會造成極其嚴重的后果。
使用的函數如果在使用之前并未定義或者可能在其他的文件中,那么就要進行聲明,函數聲明的目的就是告知編譯器函數的返回值的類型。
注意:如果一個函數沒有float、short、或者char類型的參數,在函數聲明中完全可以省略掉參數類型的說明(注意,函數定義中不能省略參數類型的說明)。這種做法依賴于調用者能夠提供數目正確且類型恰當的實參。這里,“恰當”并不意味著“等同”:float類型的參數會自動轉換為double類型,short或者char類型的參數會自動轉換為int類型。
在ANSI C標準發布之前,常常會有下面的這種聲明和定義函數的方式:
int isvowel();//聲明函數的方式 int isvowel(c) char c; { return c =='a' ; }
實際上,上面這種寫法與下面這種寫法是等價的:
int isvowel(int i) { char c; return c=='a'; }
上述兩種方式在VS2019中都是支持的。
看下面的例子:
#include<stdio.h> int main() { int i; char c; for (i = 0; i < 5; i++) { scanf("%d", &c); printf("%d ", i); } printf("\n"); return 0; }
表面上,這個程序從標準輸入設備讀入5個數,在標準輸出設備設備上寫5個數:
0 1 2 3 4
實際上,這個程序并不一定得到上面的結果。例如,在某個編譯器上,它的輸出是(當然,在VS2019環境下程序會崩潰,因為非法修改了內存空間)
0 0 0 0 0 1 2 3 4
為什么呢?問題的關鍵在于,這里的c被聲明為char類型,而不是int類型。如果程序要求scanf讀入一個整數,應該傳遞給他一個指向整數的指針。而程序中scanf函數得到的卻是一個指向字符的指針,scanf函數并不能分辨這種情況,它只是將這個指向字符的指針作為指向整數的指針而接受,并且在指針指向的位置存儲一個整數。因為整數所占的存儲空間要大于字符所占的存儲空間,所以字符c附近的內存被覆蓋。
字符c附近的內存中存儲的內容是由編譯器決定的,在本例中它所存放的是整數i的低端部分。因此,每次讀入一個數值到c時,都會將i的低端部分覆蓋為0,而i的高端部分本來就是0,相當于i每次被重新設置為0,循環將一直進行。當到達文件的結束位置后,scanf函數不再試圖讀入新的值到c。這時,i才可以正常的運行,最后終止循環。
5 檢查外部類型
注意:保證一個特定類型的所有外部定義在每個目標模塊中都有相同的類型,“相同的類型”也應該是嚴格意義上的相同。
例如,在一個文件中包含定義:
char filename[] = "/etc/passwd";
而在另一個文件中包含聲明:
extern char *filename;
在定義時,filename是一個字符數組的名稱。盡管在一個語句中引用filename的值將得到指向該數組起始元素的指針,但是filename的類型是”字符數組“,而不是字符指針。在第二個聲明中,filename被確定為一個指針。這兩種方式使用存儲空間的方式是不同的,它們無法以一種合乎情理的方式共存。第一個例子字符數組filename的內存布局如下圖所示:
第二種方式字符指針filename的內存布局如下圖所示:
修改方法如下圖所示:
char filename[] = "/etc/passwd"; extern char filename[];
也可以這樣進行修改:
char*filename = "/etc/passwd"; extern char *filename;
6 頭文件
注意:每個外部對象只在一個地方聲明,這個聲明的地方一般就在頭文件種,需要用到該外部對象的所有模塊也應該 包括在這個頭文件。特別指出的是,定義該外部對象的模塊也應該包括這個頭文件。
原文鏈接:https://blog.csdn.net/m0_57304511/article/details/123596112
- 上一篇:C語言?超詳細講解庫函數_C 語言
- 下一篇:C語言實現隨機抽取紙牌程序_C 語言
相關推薦
- 2023-04-02 vscode?ssh遠程連接服務器的思考淺析_相關技巧
- 2022-09-27 python?實現打印掃描效果詳情_python
- 2022-05-25 Shell腳本獲取jar包pid進行重啟、停止、啟動
- 2022-12-23 Python?tensorflow與pytorch的浮點運算數如何計算_python
- 2023-01-28 架構師說比起404我們更怕200錯誤_相關技巧
- 2022-10-25 Go語言實戰學習之流程控制詳解_Golang
- 2022-06-01 Python寫一個字符串數字后綴部分的遞增函數_python
- 2023-05-29 Python中如何給字典設置默認值_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支