从编写c语言源程序到运行,c语言 源代码到可执行程序的过程
從源代碼(.c)到可執行文件一共分為五個步驟:
1、編譯預處理
2、編譯階段
3、優化階段
4、匯編過程
5、鏈接程序
一、編譯預處理(.i) ------>"替代"成一個沒有宏定義、沒有條件編譯指令、沒有特殊符號的輸出文件 。
讀取c源程序,對其中的偽指令(以#開頭的指令)和特殊符號進行處理。偽指令主要包括以下四個方面:
(1)宏定義指令。
如:#define M 9 、#define SQUARE(x) ((x)*(x))?、#undef(取消宏定義),宏一般都是大寫(以區別函數)。注意:作為字符串的常量則不被替換。
#define?M?9
int?main()
{
a=M;
printf("M=%d",a);
//printf("M=%d",9);
}
#define?SQUARE(x)?((x)*(x))
//?x?*?x
int?main()
{
int?a=4;
printf("%d\n",SQURE(a+1));
//printf("%d\n",((4+1)*(4+1)));
//printf("%d\n",??4+1?*?4+1??);
}
注意:參數列表的左括號必須與SQUARE緊鄰、不能吝嗇括號,否則會出現計算邏輯的問題。
(2)條件編譯指令。
如:#ifdef,#ifndef,#else,#elif,#endif,等等。這些偽指令使得程序員可以通過定義不同的宏來決定編譯程序對哪些代碼進行處理(過濾作用),可避免重復復制代碼。
#ifdef 標識符
程序段 1
#else
程序段 2
#endif
它的功能是,如果標識符已被 #define 命令定義過則對程序段 1 進行編譯;否則對程序段 2 進行編譯。
(3)頭文件包含指令。
如:#include 、#include "stdio.h"等等。#include 是將已存在文件的內容復制到當前文件中,它可以使某些定義可以供多個不同的C源程序使用,只需加上一條#include語句 ,預編譯就會自己把頭文件中的定義統統都加入到它所產生的輸出文件中,以供編譯,而不用程序員再行定義。
注意: <> 和 "" 的區別。 <> 表示預處理到系統規定的路徑中去獲得這個文件,而 "" 則表示預處理應在當前目錄中查找文件名為?stdio.h 的文件,若沒有找到,則按系統指定的路徑信息,搜索其他目錄。
(4)特殊符號。
比如一些預定義符號__FILE__(表示正在編譯的文件的名字)、 __TIME__(表示編譯時刻的時間字符串)、 __LINE__(表示正在編譯的文件的行號)、 __DATE__(表示編譯時刻的日期字符串)、 __STDC__(判斷該文件是不是定義成標準 C 程序),預編譯程序對于在源程序中出現的這些串將用合適的值進行替換。
printf?("file:%s\tline:%d\tdate:%s\ttime:%s\n",__FILE__???,__LINE__,?__DATE__,__TIME__?);
# 運算符 和 ## 預算符
a、如果我們希望在字符串中包含宏參數,那我們就可以使用“#”,它可以把語言符號轉化為字符串。
#include
#define?SQR(x)?printf("The?square?of?"#x"?is?%d.\n",((x)*(x)))
int?main()
{
SQR(8);
}
運行結果是:The square of 8 is 64.
b、“##”就是個粘合劑,可以將前后兩部分粘合起來。
#include
#define?XNAME(n)?x?##?n
int?main()
{
XNAME(8)?;
}
運行結果是:x8
二、編譯階段(.s) ------->?翻譯成等價的中間代碼表示或匯編代碼
經過預編譯得到的輸出文件中,將只有常量。
如:數字、字符串、變量的定義,以及C語言的關鍵字(main,if,else,for,while,{,},+,-,*,\)等等。
預編譯程序,所要作的工作就是通過詞法分析、語義分析、符號匯總和語法分析,在確認所有的指令都符合語法規則之后,將其翻譯成等價的中間代碼表示或匯編代碼。
三、優化階段(執行的效率的提高)。
(1)對中間代碼的優化。
(2)與機器的硬件結構有關,比如:利用機器的各個硬件寄存器存放的有關變量的值,減少對于內存的訪問次數等等。
四、匯編過程(.o) ------> 生成相應的目標文件 。
匯編過程對匯編器比較簡單,匯編器只需將匯編代碼轉變成機器可以執行的指令,每一個匯編都有一條對應的機器指令,它沒有復雜的語法也沒有語義,也不需要指令優化,僅僅對照機器指令和匯編指令的對照表一一翻譯。
目標文件中所存放的是與源程序等效的目標的機器語言代碼。
目標文件由段組成。一個目標文件中至少有兩個段:
代碼段 主要包含的是程序的指令。該段一般是可讀、可執行、不可寫。
數據段 主要存放程序中的全局變量或靜態的數據。一般數據段都是可讀、可執行、可寫 。
五、鏈接程序 ------->將有關的目標文件彼此相連接 。
由匯編程序生成的目標文件并不能立即就被執行,其中可能還有許多沒有解決的問題,比如函數調用、模塊間的變量訪問。
鏈接包括:地址和空間分配、重定位、符號決議。
重定位:假如有一個全局變量叫var,在目標文件 A 里,而目標文件 B 里邊要使用var變量,我們編譯目標文件 B ,由于在編譯的時候編譯器找不到var的地址,編譯器在無法確定地址的情況下就將地址置為0,當鏈接器將 A 和 B 鏈接后,變量var的地址會確定下來,鏈接器將會修改地址,這地址修改的過程就稱作重定位。
鏈接處理分為兩種:
(1)靜態鏈接,就是函數的代碼將從靜態鏈接庫中被拷貝到可執行程序中(靜態鏈接庫實際上是一個目標文件的集合,其中的每個文件含有庫中的一個或者一組相關函數的代碼),但這樣使得可執行程序體積變大。
(2)動態鏈接,就是需要鏈接的代碼放到一個共享對象中,鏈接程序只用記錄可執行程序將來需要用的代碼信息,在可執行文件被執行時,動態鏈接庫的全部內容將被映射到運行時相應進程的虛地址空間, 動態鏈接程序將根據可執行程序中記錄的信息找到相應的函數代碼。 比靜態鏈接節約內存。
經過上述五個過程,C源程序就最終被轉換成可執行文件了。
總結
以上是生活随笔為你收集整理的从编写c语言源程序到运行,c语言 源代码到可执行程序的过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: antd picker 使用 如何_如何
- 下一篇: 计算机一级B考试总结500字,期中考试反