摘要:程序預(yù)處理本章節(jié)研究的是,源代碼文件是如何一步步得到一個(gè)可執(zhí)行程序的。如的語(yǔ)句被稱(chēng)為預(yù)處理指令,還有注釋文本的刪除,都在此階段完成替換。目的是能夠?qū)⑺形募械拇a組合到一起成一個(gè)完整的程序。終止程序可以正常也可以意外終止程序。
本章節(jié)研究的是,源代碼文件
test.c
是如何一步步得到一個(gè)可執(zhí)行程序test.exe
的。在之前的學(xué)習(xí)中可知.c
文件要先后經(jīng)過(guò)編譯鏈接成.exe
文件再執(zhí)行。
程序的編譯鏈接運(yùn)行如下圖所示。翻譯中編譯又包括預(yù)編譯、編譯、匯編。
編譯鏈接執(zhí)行三個(gè)步驟,都需要為其配置不同的環(huán)境。編譯和鏈接在翻譯環(huán)境中,而執(zhí)行在運(yùn)行環(huán)境中發(fā)生。
- 翻譯環(huán)境:在該環(huán)境中源代碼被轉(zhuǎn)換成可執(zhí)行的機(jī)器指令。
- 執(zhí)行環(huán)境:用于實(shí)際執(zhí)行代碼。
翻譯階段的大致流程如下圖所示。
組成一個(gè)程序的每個(gè).c
源文件都會(huì)被編譯器編譯,分別生成對(duì)應(yīng)的.obj
目標(biāo)文件。多個(gè)目標(biāo)文件以及引入的鏈接庫(kù)被鏈接器鏈接在一起,形成一個(gè)單一的.exe
可執(zhí)行程序。
編譯器即是一個(gè)用于編譯代碼的工具,在vs環(huán)境下為
cl.exe
的可執(zhí)行程序。連接器則是用于鏈接所有目標(biāo)文件的工具,在vs中為link.exe
的可執(zhí)行程序,鏈接庫(kù)是標(biāo)準(zhǔn)中任何被該程序用到的函數(shù)。如圖:
而若想觀察翻譯代碼過(guò)程中的每一個(gè)流程的具體細(xì)節(jié),在集成開(kāi)發(fā)環(huán)境
vs
中不便展示,當(dāng)然我們可以使用Linux
環(huán)境下的gcc
編譯器。
此次演示就采用加法函數(shù),分別存放在兩個(gè)文件test.c
和add.c
。
//1. add.cint Add(int x, int y){ int sum = x + y; return sum;}//2. test.c#include //聲明函數(shù)extern int Add(int x, int y);int main(){ int a = 10; int b = 20; int ret = 0; ret = Add(a, b); printf("ret = %d/n", ret); return 0;}
Linux環(huán)境下編寫(xiě)完
test.c
文件的代碼后,輸入gcc test.c -E
可以將代碼預(yù)編譯的結(jié)果輸出到屏幕上。還可以用gcc test.c -E -o test.i
是將結(jié)果輸出到文件test.i
。
如#include
,#define
,#pragma
的語(yǔ)句被稱(chēng)為預(yù)處理指令,還有注釋文本的刪除,都在此階段完成替換。
所有可以看出預(yù)編譯階段的動(dòng)作都是文本操作:
#include
頭文件的包含#define
預(yù)處理符號(hào)的替換預(yù)編譯,顧名思義,是在編譯前刪減代碼中的不必要的與機(jī)器識(shí)別代碼無(wú)關(guān)的內(nèi)容。被稱(chēng)為文本操作。
對(duì)預(yù)編譯產(chǎn)生的文件
test.i
再編譯gcc test.i -S
,會(huì)自動(dòng)生成匯編代碼test.s
。
故編譯階段是將C語(yǔ)言代碼轉(zhuǎn)化為匯編代碼,這是整體現(xiàn)象。實(shí)際上會(huì)發(fā)生這四個(gè)動(dòng)作:
詞法分析,語(yǔ)法分析,語(yǔ)義分析都是編譯器識(shí)別語(yǔ)句的操作。重點(diǎn)是接下接下來(lái)的符號(hào)匯總。
符號(hào)匯總,是只對(duì)全局符號(hào)進(jìn)行匯總,局部符號(hào)是不進(jìn)行匯總的。目的是能夠?qū)⑺形募械拇a組合到一起成一個(gè)完整的程序。如add.c
文件中的函數(shù)名Add
,還有test.c
文件中的Add
和main
。
gcc test.s -C
將編譯結(jié)束產(chǎn)生的匯編代碼轉(zhuǎn)化成了二進(jìn)制指令(機(jī)器指令)存入二進(jìn)制文件test.o
中。
匯編階段會(huì)形成符號(hào)表,因?yàn)闄C(jī)器在調(diào)用指令時(shí)需要知道其存放的位置,所謂符號(hào)表大概就是符號(hào)和其地址的集合。如圖,可以假設(shè):
鏈接將二進(jìn)制指令目標(biāo)文件
test.o
等,鏈接在一起形成可執(zhí)行程序test.out
。目標(biāo)文件test.o
是elf
格式文件,在Linux平臺(tái)下可以用readelf
翻譯并查看其內(nèi)容。
鏈接階段的動(dòng)作是:
所謂的鏈接,就是將對(duì)應(yīng)的段合并起來(lái)。
符號(hào)表的合并,是將各自的符號(hào)表合并到一起。如test.o
中的Add
的無(wú)效地址,需把add.o
中Add
的地址合并過(guò)去再重定位到變量的真實(shí)地址,才是有意義的。
從編譯期間的符號(hào)匯總,到匯編時(shí)的形成符號(hào)表,再到鏈接時(shí)的合并和重定位符號(hào)表,都是為了最后生成可執(zhí)行程序時(shí)能夠找到并鏈接各個(gè)文件中的符號(hào)。
程序首先載入內(nèi)存
有的機(jī)器上有操作系統(tǒng),這個(gè)動(dòng)作就是由操作系統(tǒng)完成,沒(méi)有的由手工完成。
執(zhí)行調(diào)用main
函數(shù)
創(chuàng)建函數(shù)棧幀
程序使用一個(gè)運(yùn)行時(shí)堆棧,存儲(chǔ)函數(shù)的局部變量和返回地址。
終止程序
可以正常也可以意外終止程序。
程序的執(zhí)行并不是本章的要點(diǎn),所以就大概介紹一下。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/122008.html
摘要:學(xué)單片機(jī)多去官網(wǎng)上查資料,下載手冊(cè),像我入門(mén)的單片機(jī)經(jīng)常去官網(wǎng),還有學(xué)的系列板子,公司的官網(wǎng)的官方例程給的很詳細(xì),在英文視角閱讀對(duì)你大有益處。 目錄 1.C語(yǔ)言經(jīng)典 2.單片機(jī)系列 3.Python方面 4.嵌入式LWip協(xié)議 5.Android 6.C++經(jīng)典書(shū)籍 7.Linux開(kāi)發(fā) ...
摘要:在符號(hào)位中,表示正,表示負(fù)。我們知道對(duì)于整型來(lái)說(shuō),內(nèi)存中存放的是該數(shù)的補(bǔ)碼。在計(jì)算機(jī)系統(tǒng)中,數(shù)值一律用補(bǔ)碼來(lái)表示和存儲(chǔ)。表示有效數(shù)字,。規(guī)定對(duì)于位的浮點(diǎn)數(shù),最高的位是 ...
摘要:如的語(yǔ)句被稱(chēng)為預(yù)處理指令,還有注釋文本的刪除,都在此階段完成替換。故宏在程序規(guī)模和執(zhí)行速度方面更勝一籌。宏替換發(fā)生在預(yù)編譯期間,故無(wú)法調(diào)試。宏可能由于運(yùn)算符優(yōu)先級(jí)的問(wèn)題,會(huì)導(dǎo)致程序出錯(cuò)。 ...
目錄 ? ?一、數(shù)據(jù)類(lèi)型介紹 二、類(lèi)型的意義 三、類(lèi)型的基本歸類(lèi) 整型家族 浮點(diǎn)數(shù)家族 構(gòu)造類(lèi)型(自定義類(lèi)型) 指針類(lèi)型 空類(lèi)型 四、整形在內(nèi)存中的存儲(chǔ) 原碼、反碼、補(bǔ)碼 大小端字節(jié)序 為什么有大端和小端? 一道經(jīng)典筆試題 ?一、數(shù)據(jù)類(lèi)型介紹 數(shù)據(jù)從大的方向分為兩類(lèi): 內(nèi)置類(lèi)型自定義類(lèi)型內(nèi)置類(lèi)型我們前面已經(jīng)學(xué)習(xí)過(guò),如下: char? ? ? ? ? ? //字符數(shù)據(jù)類(lèi)型 short? ? ? ...
閱讀 1829·2021-10-09 09:44
閱讀 2702·2021-09-22 15:38
閱讀 2499·2021-09-09 09:33
閱讀 702·2021-09-07 09:58
閱讀 1830·2021-09-02 15:41
閱讀 2515·2019-08-30 15:55
閱讀 1803·2019-08-30 15:55
閱讀 548·2019-08-30 15:44