在C語(yǔ)言中,有多少個(gè)未初始化的局部變量?答案通常是:與編譯器有關(guān)。
可以但不保證初始化為0。
不確定。
簡(jiǎn)而言之,它們都是嚴(yán)肅的形而上學(xué)的答案,這很煩人。
但是,當(dāng)有人為您談?wù)摼幾g器,C庫(kù)和處理器體系結(jié)構(gòu)時(shí),卻無(wú)法為您提供重現(xiàn)該問(wèn)題的真實(shí)場(chǎng)景,那么這個(gè)人很可能是胡說(shuō)八道。
實(shí)際上,這個(gè)問(wèn)題本身是一個(gè)錯(cuò)誤的問(wèn)題。
如果您能說(shuō)100,000個(gè)單詞,我們只需要能夠確定其在特定情況下的特定行為。
當(dāng)然,這需要設(shè)計(jì)一個(gè)相對(duì)可行的實(shí)驗(yàn)。
在演示實(shí)際代碼的行為之前,讓我給出一些知識(shí)。
CPU無(wú)法識(shí)別變量,更不用說(shuō)變量名了。
CPU只會(huì)從特定的存儲(chǔ)位置獲取值,或者將值存儲(chǔ)在特定的存儲(chǔ)位置,因此當(dāng)被問(wèn)到變量的值是什么時(shí),有必要知道該變量的值存儲(chǔ)在哪里。
考慮以下代碼:#include void func1(){int a; printf(“ func1:%d ”,a); a = 12345;} void func2(){int b; printf(“ func2:%d ”,b);} void func4(){int d; printf(“ func3:%d ”,d);} void func3(){int c; printf(“ func3:%d " c); c = 54321; func4();} void test_call(){func3();} int main(int argc,char ** argv){func1(); func2(); test_call();}我們從func1到func4一共有4個(gè)函數(shù),并且內(nèi)部有一個(gè)未初始化的局部變量。
它們的價(jià)值是什么?對(duì)于這種局部變量,其值取決于:變量在堆棧中的位置。
該變量對(duì)應(yīng)于堆棧位置是否已存儲(chǔ)過(guò)?如您所見(jiàn),上面的第一點(diǎn)標(biāo)記了一個(gè)內(nèi)存位置,第二點(diǎn)是代碼的行為,即只要有存儲(chǔ)對(duì)應(yīng)位置的代碼,以及后面的代碼(如果沒(méi)有復(fù)位值)位置的位置,位置將在存儲(chǔ)后保留原始值。
驗(yàn)證非常簡(jiǎn)單,請(qǐng)嘗試:[root @ localhost test]#./a.outfunc1:0func2:12345func3:0func3:0根據(jù)函數(shù)調(diào)用堆??蚣埽琭unc1的局部變量a和局部變量的變化而定func2的變量b顯然位于同一位置。
調(diào)用func1時(shí),這是一個(gè)新的內(nèi)存(也許在進(jìn)入main之后,堆棧幀到達(dá)了該位置之后),a的值取決于在此位置被調(diào)用到內(nèi)存中的頁(yè)面的相應(yīng)偏移量的初始值。
在操作系統(tǒng)上:將操作系統(tǒng)分配給程序頁(yè)面時(shí),操作系統(tǒng)可能會(huì)將頁(yè)面清除為零頁(yè)面。
堆棧分配不會(huì)涉及C庫(kù)。
顯然,這里不涉及C庫(kù)的行為,但是malloc分配的內(nèi)存涉及C庫(kù)。
打印的結(jié)果顯示a的值為0,我們認(rèn)為操作系統(tǒng)已向應(yīng)用程序返回了零頁(yè)。
接下來(lái),該函數(shù)將其分配給func1中的12345后返回。
當(dāng)下一次調(diào)用func2時(shí),將在以前退出func1的堆??蚣芪恢弥亟ǘ褩?蚣埽⑶蚁鄳?yīng)位置仍為12345。
在執(zhí)行func1代碼指令0的ret操作后,我沒(méi)有看到堆棧清除。
考慮因素,不應(yīng)有此類(lèi)說(shuō)明。
查看test_call函數(shù),很明顯,func3和func4不是使用同一堆棧幀來(lái)調(diào)用的,因此即使將func3中的c分配給了54321,也不會(huì)影響func4在堆棧幀上方的值d 。
因此,c和d的初始值保持為0.然后,在指令級(jí)初始化局部變量而不初始化局部變量的區(qū)別是什么?這很簡(jiǎn)單,只需用自己的眼睛看一下即可。
首先看一下未初始化的局部變量的func1:// int a; 00000000004005ad:4005ad:55 push%rbp 4005ae:48 89 e5 mov%rsp,%rbp 4005b1:48 83 ec 10 sub $ 0 x10,%rsp 4005b5:8b 45 fc mov -0x4(%rbp),%eax 4005b8:89 c6 mov%eax,%esi 4005ba:bf 90 07 40 00 mov $ 0x400790,%edi 4005bf:b8 00 00 00 00 mov $ 0x0,%eax 4005c4: e8 b7 fe ff ff callq 400480 4005c9:c7 45 fc 39 30 00 00 movl $ 0x3039,-0x4(%rbp)4005d0:c9 Leaveq 4005d1:c3 retq查看局部變量a的初始化2222版本:// int a = 2222; 00000000004005ad:4005ad:55 push%rbp 4005ae:48 89 e5 mov%rsp,%rbp 4005b1:48 83 ec 10 sub $ 0x10,%rsp 4005b5:c7 45 fc 00 00 00 00 movl $ 0x0,-0x4( %rbp)4005bc:8b 45 fc mov -0x4(%rbp),%eax 4005bf:89 c6 mov%eax,%esi 4005c1:bf 90 07 40 00 mov $ 0x400790,%edi 4005c6:b8 00 00 00 00 mov $ 0 x0,%eax 4005cb:e8 b0 fe ff ff callq 400480 4005d0:c7 45 fc 39 30 00 00 movl $ 0x3039,-0x 4(%rbp)4005d7:c9 Leaveq 4005d8:c3 retq僅缺少一條指令:4005b5: c7 45 fc 00 00 00 00 movl $ 0x0,-0x4(%rbp)初始化操作由實(shí)際指令完成