国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

這篇具有很好參考價值的文章主要介紹了深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。


深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

程序的翻譯環(huán)境和執(zhí)行環(huán)境

在ANSI C的任何一種實現(xiàn)中,存在兩個不同的環(huán)境。

第1種是翻譯環(huán)境,在這個環(huán)境中源代碼被轉(zhuǎn)換為可執(zhí)行的機器指令。
從 .c 到.exe的過程中需要依賴翻譯環(huán)境

第2種是執(zhí)行環(huán)境,它用于實際執(zhí)行代碼

翻譯環(huán)境

編譯

編譯過程其實又被細(xì)分為三個環(huán)節(jié),即預(yù)編譯,編譯和匯編

組成一個程序的每個源文件(以.c為后綴的文件)通過編譯過程分別轉(zhuǎn)換成目標(biāo)代碼(也就是以.obj為后綴的文件 )

每個目標(biāo)文件由鏈接器(linker)捆綁在一起,形成一個單一而完整的可執(zhí)行程序(.exe)。

鏈接器同時也會引入標(biāo)準(zhǔn)C函數(shù)庫中任何被該程序所用到的函數(shù),而且它可以搜索程序員個人
的程序庫,將其需要的函數(shù)也鏈接到程序中

如圖:

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

總結(jié):每個源文件 單獨經(jīng)過 編譯器 處理,生成 目標(biāo)文件 ,所有目標(biāo)文件與鏈接庫 一起,在 鏈接器的作用下生成可執(zhí)行文件

先寫一段代碼 :

#include<stdio.h>

int main()
{
	printf("hehe\n");
	return 0;
}

運行后, 查看 .exe 文件 ,.exe 其實就是可執(zhí)行程序

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)
深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

add.obj 和test.obj 這兩個目標(biāo)文件就是經(jīng)過編譯器的處理,最終生成了目標(biāo)文件( .obj)
將test.c的文件預(yù)編譯后生成test.i

預(yù)編譯

預(yù)編譯過程主要處理那些源代碼文件中的以“#”開始的預(yù)編譯指令。比如“#include”、“#define”等,主要處理規(guī)則如下:

  • 將所有的“#define”刪除,并且展開所有的宏定義。

  • 處理所有條件預(yù)編譯指令,比如“#if”、“#ifdef”、“#elif”、“#else”、“#endif”。

  • 處理“#include”預(yù)編譯指令,將被包含的文件插入到該預(yù)編譯指令的位置。注意,這個過程是遞歸進行的,也就是說被包含的文件可能還包含其他文件。

  • 刪除所有的注釋“//”和“/**/”。

  • 添加行號和文件名標(biāo)識,比如#2“hello.c”2,以便于編譯時編譯器產(chǎn)生調(diào)試用的行號信息及用于編譯時產(chǎn)生編譯錯誤或警告時能夠顯示行號。

  • 保留所有的#pragma編譯器指令,因為編譯器須要使用它們。

經(jīng)過預(yù)編譯后的.i文件不包含任何宏定義,因為所有的宏已經(jīng)被展開,并且包含的文件
也已經(jīng)被插入到.i文件中。所以當(dāng)我們無法判斷宏定義是否正確或頭文件包含是否正確時,可以查看預(yù)編譯后的文件來確定問題。

在預(yù)編譯這個環(huán)節(jié),我們主要剖析三個部分 : 頭文件的包含 、刪除注釋 、替換#define定義的符號

頭文件的包含

在預(yù)編譯階段,編譯器會將代碼中所包含的頭文件都替換成頭文件的內(nèi)容。例如,#include <stdio.h>這句代碼會被替換成stdio.h這個頭文件中的全部代碼

test.c 文件里面有#include"test.h" ,#include"test.h" 的作用是將test.h的文件拷貝一份放到 test.i中

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

刪除注釋

在預(yù)編譯階段,編譯器會將代碼中所有的注釋“//”和“/**/”

替換#define定義的符號

#define定義的標(biāo)識符也好,定義的宏也罷,都是起到替換的作用,而真正進行替換的時刻便是預(yù)編譯階段

預(yù)編譯階段做的3件事,實際上都是一些文本操作,并沒有運行該代碼

編譯

編譯過程就是把預(yù)處理完的文件進行一系列詞法分析、語法分析、語義分析及優(yōu)化后生產(chǎn)相應(yīng)的匯編代碼文件,這個過程往往是我們所說的整個程序構(gòu)建的核心部分,也是最復(fù)雜的部分之一。我們先簡單介紹編譯的具體幾個步驟,這涉及編譯原理等一些內(nèi)容

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

詞法分析

比如我們有一行C語言的源代碼如下:

array[index] = (index+4)*(2+6)

首先源代碼程序被輸入到掃描器( Scanner),掃描器的任務(wù)很簡單,它只是簡單地進行詞法分析,運用一種類似于有限狀態(tài)機(Finite State Machine〉的算法可以很輕松地將源代碼的字符序列分割成一系列的記號(Token)。
比如上面的那行程序,總共包含了28個非空字符,經(jīng)過掃描以后,產(chǎn)生了16個記號,如表所示。

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

詞法分析產(chǎn)生的記號一般可以分為如下幾類:關(guān)鍵字、標(biāo)識符、字面量(包含數(shù)字、字符串等)和特殊符號(如加號、等號)。在識別記號的同時,掃描器也完成了其他工作。比如將標(biāo)識符存放到符號表,將數(shù)字、字符串常量存放到文字表等,以備后面的步驟使用。

有一個叫做lex的程序可以實現(xiàn)詞法掃描,它會按照用戶之前描述好的詞法規(guī)則將輸入的字符串分割成一個個記號。因為這樣一個程序的存在,編譯器的開發(fā)者就無須為每個編譯器開發(fā)個獨立的詞法掃描器,而是根據(jù)需要改變詞法規(guī)則就可以了

另外對于一些有預(yù)處理的語言,比如C語言,它的宏替換和文件包含等工作一般不歸入編譯器的范圍而交給一個獨立的預(yù)處理器。

語法分析

接下來語法分析器(Grammar Parser)將對由掃描器產(chǎn)生的記號進行語法分析,從而產(chǎn)生語法樹(Syntax Tree)。
整個分析過程采用了上下文無關(guān)語法(Context-free Grammar)的分析手段,如果你對上下文無關(guān)語法及下推自動機很熟悉,那么應(yīng)該很好理解。否則,可以參考一些計算理論的資料,一般都會有很詳細(xì)的介紹。此處不再贅述。
簡單地講,由語法分析器生成的語法樹就是以表達式(Expression)為節(jié)點的樹
我們知道,C語言的一個語句是一個表達式,而復(fù)雜的語句是很多表達式的組合。
上面例子中的語句就是一個由賦值表達式、加法表達式、乘法表達式、數(shù)組表達式、括號表達式組成的復(fù)雜語句。它在經(jīng)過語法分析器以后形成如圖所示的語法樹。

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

從圖中我們可以看到,整個語句被看作是一個賦值表達式;
賦值表達式的左邊是一個數(shù)組表達式,它的右邊是一個乘法表達式;數(shù)組表達式又由兩個符號表達式組成,等等。
符號和數(shù)字是最小的表達式,它們不是由其他的表達式來組成的,所以它們通常作為整個語法樹的葉節(jié)點。
在語法分析的同時,很多運算符號的優(yōu)先級和含義也被確定下來了。比如乘法表達式的優(yōu)先級比加法高,而圓括號表達式的優(yōu)先級比乘法高,等等。
另外有些符號具有多重含義,比如星號*在C語言中可以表示乘法表達式,也可以表示對指針取內(nèi)容的表達式,所以語法分析階段必須對這些內(nèi)容進行區(qū)分。
如果出現(xiàn)了表達式不合法,比如各種括號不匹配、表達式中缺少操作符等,編譯器就會報告語法分析階段的錯誤

正如前面詞法分析有l(wèi)ex一樣,
語法分析也有一個現(xiàn)成的工具叫做yacc ( Yet AnotherCompiler Compiler )。它也像lex一樣,可以根據(jù)用戶給定的語法規(guī)則對輸入的記號序列進行解析,從而構(gòu)建出一棵語法樹。
對于不同的編程語言,編譯器的開發(fā)者只須改變語法規(guī)則,而無須為每個編譯器編寫一個語法分析器,所以它又被稱為“編譯器編譯器(CompilerCompiler)”。

語義分析

接下來進行的是語義分析,由語義分析器(Semantic Analyzer)來完成。
語法分析僅儀是完成了對表達式的語法層面的分析,但是它并不了解這個語句是否真正有意義。

比如C語言里面兩個指針做乘法運算是沒有意義的,但是這個語句在語法上是合法的;比如同樣一個指針和一個浮點數(shù)做乘法運算是否合法等。

編譯器所能分析的語義是靜態(tài)語義( StaticSemantic),所謂靜態(tài)語義是指在編譯期可以確定的語義,與之對應(yīng)的動態(tài)語義(DynamicSemantic)就是只有在運行期才能確定的語義。

經(jīng)過語義分析階段以后,整個語法樹的表達式都被標(biāo)識了類型,
如果有些類型需要做隱式轉(zhuǎn)換,語義分析程序會在語法樹中插入相應(yīng)的的轉(zhuǎn)換節(jié)點。
上面描述的語法樹在經(jīng)過語義分析階段以后成為如圖所示的形式

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

符號匯總

在這個環(huán)節(jié)中,會將每個源文件的全局范圍的變量符號進行匯總

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

匯編

匯編器是將匯編代碼轉(zhuǎn)變成機器可以執(zhí)行的指令,每一個匯編語句幾乎都對應(yīng)一條機器指令。所以匯編器的匯編過程相對于編譯器來講比較簡單,它沒有復(fù)雜的語法,也沒有語義,也不需要做指令優(yōu)化,只是根據(jù)匯編指令和機器指令的對照表一一翻譯就可以了

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

將匯編代碼翻譯成二級制指令 ,這個二進制指令存放在目標(biāo)文件中 ,同時會給每個源文件匯總出來的符號分配一個地址,然后分別生成一個符號表 ,編譯步驟里的符號匯總就是為匯編形成的符號表服務(wù)的

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

test.c文件中提取的符號Add只是Add函數(shù)的聲明,并不是定義,無法判斷Add函數(shù)是否真正存在,所以test.c生成符號表時分配給Add符號的地址是一個無意義(非法)的地址

鏈接

合并段表

(vs生成的目標(biāo)文件的后綴是.obj , gcc生成的目標(biāo)文件后綴是.o)

匯編結(jié)束后所生成的obj文件內(nèi)部會被劃分為幾個段,在鏈接過程中就會把每個obj文件對應(yīng)的段通過某種規(guī)則合并起來,最后形成可執(zhí)行程序(.exe為后綴)

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

符號表的合并和重定位

程序并不是一寫好就永遠(yuǎn)不變化的,它可能會經(jīng)常被修改。

比如我們在第1條指令之后、第5條指令之前插入了一條或多條指令,那么第5條指令及后面的指令的位置將會相應(yīng)地往后移動,原先第一條指令的低4位的數(shù)字將需要相應(yīng)地調(diào)整。

在這個過程中,我們需要人工重新計算每個子程序或跳轉(zhuǎn)的目標(biāo)地址。當(dāng)程序修改的時候,這些位置都要重新計算,十分繁瑣又耗時,并且很容易出錯。這種重新計算各個目標(biāo)的地址過程被叫做重定位

深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)

符號表并非無意義。如果需要調(diào)用某一函數(shù)時,編譯器會在符號表中查找該符號,如果有,則調(diào)用成功,否則調(diào)用失敗
符號表有來自全局變量 、函數(shù)等 ,不是所有的符號符號表都有 ,局部的變量不會存在符號表,因為局部變量只能在局部范圍內(nèi)使用 ,是不能跨文件使用

在一個程序被分割成多個模塊以后,這些模塊之間最后如何組合形成一個單一的程序是須解決的問題。
模塊之間如何組合的問題可以歸結(jié)為模塊之間如何通信的問題,最常見的屬于靜態(tài)語言的C/C++模塊之間通信有兩種方式,
一種是模塊間的函數(shù)調(diào)用,另外一種是模塊間的變量訪問。
函數(shù)訪問須知道目標(biāo)函數(shù)的地址,變量訪問也須知道目標(biāo)變量的地址,所以這兩種方式都可以歸結(jié)為一種方式,那就是模塊間符號的引用。
模塊間依靠符號來通信類似于拼圖版,定義符號的模塊多出一塊區(qū)域,引用該符號的模塊剛好少了那一塊區(qū)域,兩者一拼接剛好完美組合,模塊的拼接過程就是鏈接(Linking)。

執(zhí)行環(huán)境

exe程序執(zhí)行的過程大概可以分為四個步驟:

程序首先要載入內(nèi)存中。 在有操作系統(tǒng)的環(huán)境中,該操作一般由操作系統(tǒng)來完成。在獨立的環(huán)境中,程序的載入可以由手工完成,也可以通過可執(zhí)行代碼置入只讀內(nèi)存來完成。
程序的執(zhí)行開始。 接著便調(diào)用main函數(shù)。
開始執(zhí)行程序代碼。 這個時候程序?qū)⑹褂靡粋€運行時堆棧(stack),存儲函數(shù)的局部變量和返回地址。程序同時也可以使用靜態(tài)(static)內(nèi)存,存儲于靜態(tài)內(nèi)存中的變量在程序的整個執(zhí)行過程一直保留它們的值。
終止程序。 正常終止main函數(shù),也可能是意外終止。

如果你覺得這篇文章對你有幫助,不妨動動手指給點贊收藏加轉(zhuǎn)發(fā),給鄃鱈一個大大的關(guān)注
你們的每一次支持都將轉(zhuǎn)化為我前進的動力?。。?/strong>文章來源地址http://www.zghlxwxcb.cn/news/detail-437630.html

到了這里,關(guān)于深度挖掘.c到.exe的整個過程,透過現(xiàn)象看本質(zhì)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包