從一道面試題來看計算機專業(yè)基礎知識的重要性
以前碰到過這樣一道面試題:
// 請分析如下代碼的執(zhí)行結果。int main(){????int?i?=?0;????int?arr[3]?=?{0};for(;i<=3;i++) {arr[i] = 0;printf("Hello, World! \n");}return 0;}
我心想,頭一次面試碰到這么簡單的面試題,面試官你是不是看不起我?
奮筆疾書,我寫了個三行?Hello,World。
可是當我自信滿滿的交給面試官的時候他卻詭異的笑了,仿佛在說,小伙計,你太天真了,你是不是看不起我出的題?
這一笑笑得我是冷汗直冒,就跟偷偷打飛機被人發(fā)現(xiàn)了一樣,異常尷尬。
當然,以上都是我胡謅出來的,一個面試不可能有這么多內心戲,我是一個寫代碼的程序員,又不是戲精,哪來那么多想法。
但是,雖然內心戲是胡謅的,可是這道面試題目確實實打實的。
接下來我們仔仔細細分析下這道題目,為了先快速得到執(zhí)行結果,我們先運行一遍看看:
執(zhí)行結果可以看到,這段代碼竟然無限循環(huán)了,是不是很難理解。
想分析這段代碼,我們首先需要了解數(shù)組這個數(shù)據(jù)結構,簡單來說:數(shù)組是用一組連續(xù)的內存空間,存儲一組具有相同類型的數(shù)據(jù)的線性數(shù)據(jù)結構。
數(shù)據(jù)邏輯結構根據(jù)數(shù)組的邏輯結構圖我們可以總結出一位數(shù)組的尋址公式為:arr[i]_address = base_address + i*data_size,其中?data_size?表示每個數(shù)據(jù)的磁盤空間大小。
根據(jù)這個尋址公式,我們來對上邊的代碼做出尋址行為的分析,當 i = 3 時,理論上尋址公式為?arr[3]_address = arr[2]_address + i*data_size,此處需要注意的是因為 C 語言中,數(shù)組越界是一種未決行為,如果你是從事 Java 或者其他高級語言的話會發(fā)現(xiàn)對于數(shù)組越界是當做一種異常行為來處理的,但是 C 語言不是。對于 C 語言來說,只要不是訪問受限的內存空間,所有的內存空間都是可以自由訪問的,哪么根據(jù)既定尋址公式,arr[3]會被定位到某塊本不屬于數(shù)組的內存地址上,而這塊地址恰恰存儲的是我們定義的變量 i,也就是說此時?arr[3] = 0?,相當于 i = 0 時的情況,這樣就會導致無限循環(huán)。
如果i<=3改成i<3,結果才是我們想要的樣子。
我們想要的結果到此,程序的執(zhí)行結果我們分析完了,但是你可能對于上邊加粗的?而這塊地址恰恰存儲的是我們定義的變量 i?這句話有疑問,怎么就?arr[3] = 0?了呢?
這個時候我們大學學過的操作系統(tǒng)和計算機體系結構以及甚至可能是編譯原理的知識就要排上用場啦。
我們都知道在寫一個函數(shù)時會使用形參,形參實例化時會形成一份拷貝,調用這個函數(shù)時會把實參傳進去,調用完之后那些臨時拷貝又被釋放,那么計算機在調用函數(shù)時是如何進行形參的保存和釋放的呢?這個時候會用到一個叫棧的數(shù)據(jù)結構。
棧用于維護函數(shù)調用的上下文,離開了棧,函數(shù)調用就無法實現(xiàn)。棧是從高地址向低地址延伸的。每個函數(shù)的每次調用都有它自己獨立的一個棧幀,這個棧幀中有它所需要的各種信息。
回到上邊那段代碼,產生死循環(huán)的第一個原因就是因為函數(shù)調用棧的特殊性:函數(shù)體內的局部變量是存在棧上的,且是連續(xù)壓棧。在 Linux 進程的內存布局中,棧區(qū)在高地址空間,從高向低增長。變量 i 和 arr 在相鄰地址,且 i 比 arr 的地址大,所以 arr 越界正好訪問到i。當然,前提是 i 和 arr 元素要同類型,否則那段代碼仍是未決行為。
還有另一個原因是因為編譯器分配內存和字節(jié)對齊,我們定義 3 個元素的數(shù)組加上一個變量 i 。4 個整數(shù)剛好能滿足 8 字節(jié)對齊 所以 i 的地址恰好跟著?arr[2]?后面導致死循環(huán)。如果數(shù)組本身有 4 個元素 則這里不會出現(xiàn)死循環(huán)。因為編譯器 64 位操作系統(tǒng)下,默認會進行 8 字節(jié)對齊 變量 i 的地址就不緊跟著數(shù)組后面了。
通過今天這道看似簡單實則還是比較復雜的題目,可以說坑很多,涉及到的知識點也不少,但恰恰這些知識點是我們大學學過的一些計算機基礎知識,沒有涉及任何框架,也沒有任何的新技術,可很多人還是答不上來。
簡言之,你別看有些人整天這框架那框架的玩的很嗨,但是它的計算機專業(yè)素養(yǎng)是遠遠不夠的,職業(yè)生涯前期可能不會有什么區(qū)別,但是長遠來看,掌握了基礎知識的人上限勢必要高一些。
從今天開始,本公眾號也會更新一些計算機基礎知識的專欄,自打我停止更新這段時間,我也思考了很多,總覺得每天都很焦慮,玩著手機的時候焦慮沒有好好學習,學習的時候又心心念著電視劇劇情走向,今天學這個框架明天學那個框架,到頭來發(fā)現(xiàn)不但啥都沒學會,原來會的東西還給忘了。
5 月份開始,我會陸陸續(xù)續(xù)更新算法和數(shù)據(jù)結構、計算機網(wǎng)絡、操作系統(tǒng)原理以及設計模式等和編程語言不強相關的一些內容,希望大家能喜歡。
?往期推薦?
?
IntelliJ IDEA 2020.1 穩(wěn)定版發(fā)布
如果你喜歡這篇文章,歡迎在看轉發(fā)
生活很美好,明天見
