前言
作爲嵌入式軟件工程師,跳槽的時候通常會先來一份C/C++面試題。這裏總結一下自己或者網友遇到的C/C++面試題,供大家參考下。
實例
1、零值比較
//bool類型的零值比較
bool flag;
if(flag){
if(!flag){
}
}
//float類型的零值比較
float x;
const float EPSINON = 0.00001
if((x >= -EPSINON) && (x <= EPSINON))
//指針類型的零值比較
char *p;
if(p == NULL){
if(p != NULL){
}
}
2、sizeof
sizeof用於計算一個變量或者類型所佔的內存大小,返回單位爲字節;
char str[] = "Hello";
char *p = str;
int n = 10;
sizeof(str) = 6;//Hello佔5個字節,結尾結束符佔一個字節
sizeof(p) = 4;//一般32位機器,指針類型變量佔4個字節
sizeof(n) = 4;//一般32位機器,int類型變量佔4個字節
void Func(char str[100]){
sizeof(str) = 4;//函數未被調用時,形參不分配內存;函數調用時,形參被分配內存,直到函數執行完
//畢;傳遞的str[100]是個數組,因此傳入函數的形參可以理解爲str[100]的地址,
//所以佔4個字節
}
void *p = malloc(100);
sizeof(p) = 4;//同指針類型解釋
3、變量不同類型的定義
int a;//一個整型數
int *a;//一個指向整型數的指針
int **a;//一個指向指針類型的指針,其指向的指針指向一個整型數
int a[10];//一個有10個int型元素的數組
int *a[10];//一個有10個指向int型數據的指針類型元素的數組
int (*a)[10];//一個數組指針,指向有10個int型元素的數組
int (*a)(int);//一個函數指針,該函數有一個int型參數並返回一個int型數據
int (*a[10])(int);//一個有10個指針的數組,該指針指向一個函數,該函數有一個int型參數並返回一個int
//型數據
4、
5、
6、
7、
8、
9、
10、
11、
12、
13、關鍵字volatile有什麼含義?並給出三個不同的例子。
A、一個定義爲volatile的變量是說這個變量可能會被意外改變,這樣,編譯器就不會去假設這個變量的值。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器裏面的備份。
B、例子
1)並行設備的硬件寄存器(如:狀態寄存器)
2)一箇中斷服務子程序中會訪問到的非自動變量
3)多線程應用中被幾個任務共享的變量
14、
15、頭文件中的ifndef/define/endif是爲了防止該頭文件被重複引用。
16、#include <filename.h>和#include "filename.h"有什麼區別?
#include <filename.h>是告訴編譯器,優先從標準庫開始搜索filename.h;
#include "filename.h"是告訴編譯器,優先從用戶的工作路徑開始搜索filename.h;
17、const有什麼作用?
(1)const可以修飾常量
(2)被const修飾爲只讀的變量、函數的參數、返回值會受到保護,可以防止被意外地改變,能提高程序的健壯性。
18、簡單說明一下關鍵字static和volatile的作用?
(1)static修飾一個局部變量時,生命域是全局的,作用域是局部的;
static修飾一個C源文件內的全局變量時,生命域是全局的,作用域減小隻作用於本文件;
static修飾一個函數時,生命域是全局的,作用域減小隻作用於本文件;
(2)volatile表示被修飾的變量是易變的,警告編譯器每次都要從變量的內存地址讀取,而不是讀寄存器的備份;
(3)const和volatile可以同時修飾一個變量,例如:只讀的狀態寄存器;
const和static不可以同時修飾一個變量,因爲C++編譯器在實現const的成員函數的時候爲了確保該函數不能修改類的實例的狀態,會在函數中添加一個隱式的參數const this *。但當一個成員爲static的時候,該函數是沒有this指針的。也就是說此時static的用法和static是衝突的。
19、鏈表和數組的區別?
(1)在內存中,數組是一塊連續的區域,鏈表則是隨機的離散區域。
(2)數組的查找讀取數據塊,因爲它是連續的,知道一個數據地址就能找到對應的數據;但是數組的數據插入和刪除效率低,對內存空間的要求高,必須由足夠的連續空間,有可能會造成內存浪費。
(3)鏈表的數據插入和刪除效率高,內存利用率高且拓展靈活;但是查找讀取數據慢,必須使用遍歷的方式查找數據。
20、怎麼理解中斷的過程?
中斷的過程可以順序分爲4個部分:入棧,取向量,更新寄存器和異常返回;
(1)入棧:保存現場,數據總線依次把xPSR,PC,LR,R12和R3~R0壓棧;
(2)取向量:指令總線從向量表取出服務函數入口地址;
(3)更新寄存器:更新堆棧指針SP,PSR,PC(PC指向服務函數入口地址),LR;
(4)異常返回:恢復現場(數據出棧),更新NVIC寄存器;
Tips:LR(R14)是存儲返回地址,所以當調用一級深度的子程序返回時,可以直接讀取LR中的返回地址而不用去讀內存;當多級深度時就需要把上一級的返回地址壓入堆棧;
21、中斷服務函數能有輸入參數和返回值嗎?爲什麼?
中斷服務函數不能有輸入參數和函數返回。因爲中斷從本質來說是硬件外設自動產生的一種電信號,由硬件自身調用,沒有程序去傳遞參數給硬件,硬件也不會接收參數。再者中斷處理要越快越好以確保實時操作系統內的實時性,如Linux系統的硬中斷和軟中斷機制。
22、怎麼理解信號量、互斥信號量?
(1)信號量就是一個用來表示某個資源被佔用情況的標誌。
信號量的功能:A、實現任務間的同步;B、管理多個共享資源;
(2)互斥信號量就是一個值只有0和1兩種情況的二值信號量,實現對資源的互斥訪問。
互斥信號量的功能:A、防止任務優先級反轉問題;
23、簡單說一下ARM處理器架構和C51處理器架構的區別?
(1)ARM是32位RISC(精簡指令集)微處理器架構的總稱;C51是8位CISC(複雜指令集)微處理器架構的總稱;
(2)ARM採用馮諾依曼或者哈佛的內核結構,如Cortex-M3就採用的是哈佛結構;C51採用馮諾依曼的機構內核;
24、簡單說一下,UCOS II和UCOS III的區別?
(1)UCOS II只有0~63個優先級,而且優先級不能重複;UCOS III允許幾個任務使用同一個優先級,在同一個優先級裏面,支持時間片調度。
(2)UCOS III允許用戶在程序運行中動態配置實時操作系統內核資源,避免用戶在編程中出現資源不夠用的問題。
(3)UCOS II最大支持256個任務;UCOS III支持任意的任務、信號量、消息列表和內存塊容量,只受限於用戶CPU可以使用的RAM容量;
25、簡單說一下,UCOS II中消息隊列、消息郵箱和信號量的區別?
(1)用信號量進行行爲同步時,只能提供同步的時刻信息,不能提供內容信息。若被控制方要求得到控制方的內容信息時,可以使用消息郵箱或消息隊列。
(2)但由於消息郵箱裏面只存放一條消息,所以使用消息郵箱進行任務同步時,需要滿足一個條件:消息的產生速度總要慢於消息的消費速度,即被控制任務總是在等待消息,否則會導致消息丟失。
(3)若遇到出現消息的產生速度可能快於消息的消費速度的情況時,則可以使用比消息郵箱更爲強大的消息隊列,由於消息隊列可以存放多條消息,所以消息隊列能夠有效解決消息的臨時堆積問題。但消息隊列的使用仍然需要滿足一個條件:消息的平均產生速率比消息的平均消費速率低,否則再長的消息隊列也會溢出。
26、簡單說一下,可重入函數和不可重入函數?
(1)可重入函數:重入意味着這個函數可以重複進入,可以被並行調用,可以被中斷,它只使用自身棧上的數據變量,它不依賴於任務環境,在多任務調度過程中,是安全的,不必擔心數據出錯;
(2)不可重入意味着不可被並行調度,否則會產生不可預料的結果,這些函數體內一般使用了全局變量、靜態(static)的數據結構,使用了malloc或者free函數,使用了標準I/O函數等等;
(3)可理解爲兩者互斥,凡不是不可重入函數的就是可重入函數;