更新時(shí)間:2025-03-21 15:20:18作者:佚名
誰叫我的主
現(xiàn)在最重要的是要跟上趨勢(shì),因此,如果您使用更時(shí)尚的單詞,誰會(huì)觸及我的奶酪。誰叫我的主人?但是,作為計(jì)算機(jī)工作人員,我建議每個(gè)人不要遵循趨勢(shì)。 Java今天很受歡迎,.NET明天很受歡迎,因此請(qǐng)學(xué)習(xí)任何時(shí)尚的東西。我的意思是花幾年時(shí)間來學(xué)習(xí)基本技能,當(dāng)您遵循趨勢(shì)時(shí),您將能夠以一半的努力取得兩倍。不再胡說八道。
我們都聽到這樣的說法:“主要是C語言的入口”。我仍然不明白為什么我這么說。就像有人說:“賺錢正在接女孩,”他一定被無數(shù)磚頭打了一巴掌。這句話應(yīng)該是“賺錢是接女孩的條件,但是這種情況尤其重要?!蹦敲瓷厦娴木渥討?yīng)該是“主要是C語言中的符號(hào),但此符號(hào)非常特別?!?/p>
讓我們看以下示例:
/ *文件名test00.c */
int main(int argc,char* argv)
返回0;
編譯并鏈接它:
cc test00.c -o test.exe
test.exe將生成
但是我們添加了此選項(xiàng):-nostdlib(不鏈接到標(biāo)準(zhǔn)庫)
cc test00.c -nostdlib -o test.exe
鏈接器將報(bào)告一個(gè)錯(cuò)誤:
未定義的符號(hào):__ -start
也就是說:
1。編譯器默認(rèn)值以找到__啟動(dòng)符號(hào),而不是主要的符號(hào)
2。符號(hào)__ -start是程序的起點(diǎn)
3。主是標(biāo)準(zhǔn)庫調(diào)用的符號(hào)
讓我們考慮另一個(gè)問題:
當(dāng)我們編寫一個(gè)程序(例如模塊)時(shí),我們通常需要初始化和去定位,但是為什么有些模塊在編寫C程序時(shí)沒有這兩個(gè)過程?例如,我們的程序可以雜亂無章,沒有主機(jī),但我們不會(huì)在Main中初始化堆。例如,您可以直接在Main中打印F,但是我們沒有打開標(biāo)準(zhǔn)輸出文件。 (那些不知道stdin,stdout,stderr和printf和stdout關(guān)系的人,請(qǐng)先查看C語言中文件的概念)。
有人說這些事情不需要初始化。如果您真的必須這樣想,請(qǐng)不要再讀任何內(nèi)容。我個(gè)人認(rèn)為計(jì)算機(jī)軟件不適合您。
聰明的人會(huì)認(rèn)為他們必須在主要領(lǐng)先之前做過一些事情。直接調(diào)用這些功能而無需初始化。通常,我們會(huì)在編譯器環(huán)境中找到一個(gè)類似于CRT0.O的名稱的文件,該名稱包含我們剛才提到的__ -start符號(hào)。 (CRT可能是C運(yùn)行時(shí)的縮寫,請(qǐng)幫助確認(rèn)。)
那么,真正的CRT0.S是什么樣的?在這里,我們給出一些偽代碼:
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
部分.TEXT:
__開始:
init stack;
初始化堆;
開放性stdin;
打開stdout;
開放式stderr;
推動(dòng)argv;
推動(dòng)argc;
致電_main; (呼叫主)
貧困堆;
關(guān)閉stdin;
關(guān)閉stdout;
關(guān)閉stderr;
致電__ exit;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
實(shí)際上,可能有很多初始化工作,因?yàn)樗鼈兌寂c操作系統(tǒng)有關(guān),因此作者不會(huì)一個(gè)一個(gè)列出它們。
注意:
1。不同的編譯器不一定具有默認(rèn)符號(hào)__ -start。
2。_組裝中的詞是C語言中的主要內(nèi)容,因?yàn)閰R編器和C編譯器具有不同的命名符號(hào)(通常是一個(gè)下劃線'_')。
3。目前,操作系統(tǒng)結(jié)構(gòu)的兩個(gè)主要分支:微核和麥克羅內(nèi)爾。微動(dòng)物的優(yōu)點(diǎn)是它們具有清晰的結(jié)構(gòu)和簡(jiǎn)單的結(jié)構(gòu),較少的內(nèi)核組件,易于維護(hù);缺點(diǎn)是流程之間存在更多的溝通,程序經(jīng)常進(jìn)入和退出內(nèi)核,并且其效率較低。宏內(nèi)核恰恰相反。目的是什么:無法確保在用戶空間中初始化每個(gè)組件(標(biāo)準(zhǔn)庫功能)。某些組件可能不會(huì)初始化。創(chuàng)建過程時(shí),操作系統(tǒng)在內(nèi)核空間中進(jìn)行操作。這取決于操作系統(tǒng)的特定實(shí)現(xiàn)。例如,堆,宏內(nèi)核結(jié)構(gòu)可以在內(nèi)核中初始化,并且微核結(jié)構(gòu)可以在用戶空間中。即使是相同的微核,也可以在內(nèi)核空間中初始化此東西。
隨著CPU技術(shù)的發(fā)展,存儲(chǔ)量的迅速擴(kuò)展和代碼復(fù)雜性的增加,微動(dòng)物越來越多地采用。您會(huì)提高代碼的復(fù)雜性以達(dá)到10%的效率嗎?您應(yīng)該知道,CPU速度每18個(gè)月將翻一番。因此,我對(duì)程序員的要求是,我不希望您的代碼先提高效率,并且我希望您的代碼能夠快速理解和維護(hù)80%的人。
總結(jié):
在執(zhí)行主函數(shù)之前,它主要初始化系統(tǒng)相關(guān)資源:
1。設(shè)置堆棧指針
2。初始化靜態(tài)和全局變量,即數(shù)據(jù)段的內(nèi)容
3。分配非初始化部分的初始值:數(shù)字短,int,long等。為0linker是什么意思,bool是錯(cuò)誤的,指針為null等,也就是.bss段的內(nèi)容
4。運(yùn)行全局構(gòu)造函數(shù),可能是C ++中類似的構(gòu)造函數(shù)。
5。將主函數(shù)參數(shù),argc,argv等傳遞給主函數(shù),然后實(shí)際運(yùn)行主函數(shù)
主要功能之前發(fā)生了什么
一些微妙的細(xì)節(jié)與本文的主題無關(guān)。我們不首先關(guān)注它。我們只需要知道啟動(dòng)代碼是設(shè)置矢量表,然后跳到__ Main函數(shù)。跳到代碼段的特定部分如下:
[c-sharp]查看platercopy
reset_handler procexport reset_handler [弱]導(dǎo)入__ mainldr r0,= __ mainbx r0endp
當(dāng)您看到__ -Main功能時(shí),估計(jì)許多人認(rèn)為這是主要功能或編譯名稱的別名,否則與MAIN相關(guān)的單詞將不再在啟動(dòng)代碼中找到。但是事實(shí)是貝語網(wǎng)校,__ -Main和Main是完全不同的功能!如果這還不足以讓您感到驚訝,請(qǐng)讓我告訴您另一個(gè)事實(shí):您找不到__框架代碼,因?yàn)檫@是由編譯器自動(dòng)創(chuàng)建的!
如果您仍然對(duì)此持懷疑態(tài)度,則可以檢查MDK文檔,并找到一個(gè)描述:當(dāng)鏈接看到Main()的定義時(shí),它將自動(dòng)創(chuàng)建。簡(jiǎn)而言之,當(dāng)編譯器發(fā)現(xiàn)主函數(shù)被定義時(shí),它將自動(dòng)創(chuàng)建__ -main。
我們基本上已經(jīng)弄清楚了__ Main功能的起源,所以現(xiàn)在的問題是,這與Main有什么關(guān)系?實(shí)際上,__Main主要做兩件事:初始化C/C ++所需的資源并調(diào)用主函數(shù)。暫時(shí)不會(huì)討論初始化,但是“調(diào)用主函數(shù)”的功能可以幫助我們解決有關(guān)為什么稱為__ main的啟動(dòng)代碼的疑問,但最后它可以轉(zhuǎn)向主函數(shù)。
如果初始化C/C ++所需的資源與特定情況分開,那么很難清楚地解釋。讓我們首先看一下編譯的裝配代碼段:
以__RT開頭的任何東西都用于初始化C/C ++運(yùn)行時(shí)庫;從__ -scatterload開始時(shí),根據(jù)離散文件的定義將代碼中的變量映射到相應(yīng)的內(nèi)存位置。在本文開頭回答問題的關(guān)鍵是__ -scatterload_copy函數(shù)!
讓我們?cè)赟TM32F10X平臺(tái)上舉一個(gè)簡(jiǎn)單的示例。首先,我們需要了解平臺(tái)的閃存地址從0x08000000開始,主要是存儲(chǔ)代碼。當(dāng)SRAM以0x20000000開頭,也就是說,內(nèi)存。然后C/C ++具有此代碼線:
[CPP]查看Platecopy
靜態(tài)int g_ival = 12;
當(dāng)我們的程序開始運(yùn)行時(shí),我們通過IDE發(fā)現(xiàn)g_ival映射到內(nèi)存地址0x20000000,并且該值是一個(gè)隨機(jī)數(shù)0xffffbe00,而不是代碼中的12個(gè)設(shè)置,如圖所示:
我們讓程序繼續(xù)執(zhí)行。執(zhí)行__ -scatterload_copy之后,我們發(fā)現(xiàn)g_ival已成為我們需要的初始值:
接下來是C/C ++庫的初始化,最后輸入了主函數(shù),此時(shí)一切都準(zhǔn)備就緒。
如果您僅限于桌面應(yīng)用程序的開發(fā),因?yàn)榫幾g的程序具有許多操作系統(tǒng)功能,它將給我們帶來極大的困惑linker是什么意思,以了解該程序的操作。只有進(jìn)入嵌入式字段并在無需操作系統(tǒng)的支持的情況下,我們才能更好地了解軟件的運(yùn)行方式。只有在這個(gè)時(shí)候,我們才能更清楚地知道,主要功能不是起點(diǎn)。
最后,如果您想學(xué)習(xí)C/C ++,則可以向編輯器“ 01”發(fā)送私人消息,以獲取材料,開發(fā)工具和聽力權(quán)限!