練一練!3道經典嵌入式C 面試題,答案在文末
來自專欄韋東山嵌入式Linux
7 人贊了文章
題一,堆和棧的區別是?
題二,Volatile與Register的區別是?
題三,ARM里的大端格式和小端格式分別是什麼意思?
題一答案:
(1)存儲內容不同
棧:在函數調用時,棧中存放的是函數中(最底下是函數調用後的下一條指令)的各個參數(局部變數)。
堆:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體內容由程序員分配。
(2)管理方式上不同
棧:由系統自動分配並釋放空間。 例如,聲明在函數中一個局部變數 int b; 系統自動在棧中為b開闢空間,當對應的生存周期結束後棧空間被自動釋放。
堆:需要程序員指定大小手動申請和手動釋放,在C語言中使用malloc函數申請,使用free函數釋放。
(3)空間大小不同
棧:獲取空間較小。在Windows下一般大小是1M或2M,當剩餘棧空間不足時,分配失敗overflow。
堆:獲得空間根據系統的有效虛擬內存有關,比較靈活、大。
(4)能否產生碎片不同
棧:不會產生碎片,空間連續。
堆:採用的是鏈表的存儲方式,會產生碎片。
(5)生長方向不同
棧: 向低地址擴展的數據結構,是一塊連續的內存區域。
堆: 向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表空閑內存地址來存儲的,自然不連續,而鏈表的遍歷方向是由低地址向高地址。
(6)分配方式不同
棧:有2種分配方式:靜態分配和動態分配,靜態由編譯器完成,例如局部變數;動態由malloc函數實現,由編譯器進行釋放。
堆: 都是動態分配的,沒有靜態分配的堆。
(7)分配效率不同
棧:由系統自動分配,速度較快。但程序員無法控制。
堆:由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便。
題二答案:
a.volatile
volatile是易變的,不穩定的意思,volatile是關鍵字,是一種類型修飾符,用它修飾的變數表示可以被某些編譯器未知的因素更改,比如操作系統、硬體或者其他線程等,遇到這個關鍵字聲明的變數,編譯器對訪問該變數的代碼不在進行優化,從而可以提供對特殊地址的穩定訪問。那麼什麼是編譯器優化呢?
為了提高運行效率,攻城師們費盡心機地把代碼優化,優化程序運行時存取速度。一般,分為硬體優化和軟體優化。硬體優化,流水線工作,詳細可以參考《計算機組成原理》。軟體優化,一部分是程序猿們做的代碼優化(前提你得有優化的思路和能力),還有一部分就是我們的編譯器優化了。
現代的編譯器經過那麼多年的發展,已經比較成熟,它會把多餘的變數忽略掉,讓代碼的運行效率更高。默認情況下,編譯器都會對代碼進行優化,會把一些變數在寄存器里存取,而不是在內存里存取,如此一來,CPU在自己家裡拿東西當然比從內存那裡拿東西要快得多。舉個小栗子:
int i = 5;
int a = i;
……
int b = i;
編譯器發現兩次從i讀數據的代碼之間,並沒有對i進行過操作,它會自動把上次讀的數據放在b中,而不是重新從i裡面讀取。
而volatile關鍵字告訴編譯器該變數是隨時可能發生變化的,每次使用它的時候必須從內存中取出它的值,因而編譯器生成的彙編代碼會從原內存地址中讀取數據使用,而不是從寄存器或者緩存中讀取,從而保證了對特殊地址的穩定訪問。
簡言之,狀態要經常變化的,為了防止我們編譯優化而導致的存取的數據不同步的問題,這時我們就需要用到volatile。那具體到什麼場景下需要用到volatile關鍵字呢?
1、並行設備的硬體寄存器(如:狀態寄存器);
2、一個中斷服務子程序中會訪問到的非自動變數();
3、多線程應用中被幾個任務共享的變數;
上面提到了非自動變數,這裡進一步對幾種變數做一番解釋:
自動變數:是在函數內部定義和使用的變數,它是局部變數。
非自動變數:有兩種,一種是全局變數,一種是靜態變數。
全局變數:在函數外面定義的變數,只能定義一次,不能有重複的定義,不然就會發生錯誤,而其他的文件要想使用這個變數,需要extern來聲明這個變數(也可省略,因為默認就是extern),這個聲明叫做引用聲明。
若不想被其他文件訪問,則用static關鍵字聲明為靜態變數。靜態變數與自動變數的本質區別是,靜態變數並不像自動變數那樣使用堆棧機制來使用內存。
而是為靜態變數分配固定的內存,在程序運行的整個過程中,它都會被保持,而不會被銷毀。這就是說靜態變數的持續性是程序運行的整個周期。這有利於我們共享一些數據。
如果靜態變數在函數內部定義,則它的作用域就是在這個函數內部,僅在這個函數內部使用它才有效,但是它不同於自動變數,自動變數離開函數後就會被銷毀,而靜態變數不會被銷毀。他在函數的整個運行周期內都會存在。
b. register
這個關鍵字請求編譯器儘可能的將變數存在CPU內部寄存器中,而不是通過內存定址訪問,以提高效率。注意是儘可能,不是絕對。你想想,一個CPU 的寄存器也就那麼幾個或幾十個,你要是定義了很多很多register 變數,它累死也可能不能全部把這些變數放入寄存器吧。
題三答案:
當前的存儲器,多以byte為訪問的最小單元,當一個邏輯上的地址必須分割為物理上的若干單元時就存在了先放誰後放誰的問題, 於是端(endian)的問題應運而生了, 對於不同的存儲方法, 就有大端(big-endian)和小端(little- endian)兩個描述。
位元組排序按分為大端和小端,概念如下
大端(big endian): 低地址存放高有效位元組
小端(little endian): 低位元組存放低有效位元組
現在主流的CPU, intel系列的是採用的little endian的格式存放數據,而motorola系列的CPU採用的是big endian,ARM則同時支持 big和little。
舉個例子
int a = 0x12345678;
a是四位元組的int類型變數,需要佔四個位元組空間,假設變數a的首地址是0x2000,那麼數據存儲在地址中的格式如下:
欲獲取更多嵌入式乾貨,請關注威信公眾號baiwenkeji
推薦閱讀:
※重磅!中國科學家最新醫學AI成果榮登《細胞》雜誌
※eeprom和flash的區別
※細說國嵌的幾宗罪——國嵌視頻
※單片機和Linux都想學_換個兩全的方法學習單片機
※嵌入式stm32項目開發 心率檢測儀的設計與實現
