C语言面试题汇总(持续更)「建议收藏」
筆者最近在找工作,因此對(duì)應(yīng)聘C/C++嵌入式開發(fā)工程師容易被問到,或者經(jīng)常搞不清楚的問題做一個(gè)匯總,也希望能對(duì)找工作的小伙伴起到幫助參考的作用。本篇集中于C語言方面的面試題目。
因?yàn)槭亲约嚎偨Y(jié)的,可能會(huì)存在錯(cuò)誤,還煩請(qǐng)各位讀者批評(píng)指正。
一、變量?jī)?nèi)存分配
1.一個(gè)由C/C++編譯的程序占用的內(nèi)存分為以下幾個(gè)部分:
①棧區(qū) —— 局部變量 —— 向低地址生長(zhǎng) —— 自動(dòng)釋放 ——其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
②堆區(qū)—— 向高地址生長(zhǎng) —— 手動(dòng)分配、釋放的存儲(chǔ)區(qū) —— malloc,free——它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表
③全局/靜態(tài)存儲(chǔ)區(qū)static—— 全局變量,靜態(tài)變量,程序運(yùn)行結(jié)束后自動(dòng)釋放
④常量存儲(chǔ)區(qū)const —— 常量字符串儲(chǔ)存在這里。儲(chǔ)存在常量區(qū)的只讀不可寫。程序運(yùn)行結(jié)束后自動(dòng)釋放
⑤代碼區(qū) ——存放函數(shù)體的二進(jìn)制代碼。
- 靜態(tài)內(nèi)存分配:編譯時(shí)分配,包括:全局、靜態(tài)全局、靜態(tài)局部
- 動(dòng)態(tài)內(nèi)存分配:運(yùn)行時(shí)分配:包括:棧(局部變量),堆(C語言常用到的變量被動(dòng)態(tài)地分配到內(nèi)存當(dāng)中:malloc,calloc,realloc,free函數(shù))
——> const修飾的全局變量也儲(chǔ)存在常量區(qū);
——> const修飾的局部變量依然在棧上。
int a = 0; //全局初始化區(qū) char *p1; //全局未初始化區(qū) main() { int b; //棧 char s[] = "abc"; //棧 char *p2; //棧 char *p3 = "123456"; //123456int a = 0; //全局初始化區(qū) char *p1; //全局未初始化區(qū) main() { int b; //棧 char s[] = "abc"; //棧 char *p2; //棧 char *p3 = "123456"; //123456\0在常量區(qū),p3在棧上。 static int c =0; //全局(靜態(tài))初始化區(qū) p1 = (char *)malloc(10); p2 = (char *)malloc(20); //分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。 strcpy(p1, "123456"); //123456\0放在常量區(qū),編譯器可能會(huì)將它與p3所指向的"123456"優(yōu)化成一個(gè)地方。 }在常量區(qū),p3在棧上。
static int c =0; //全局(靜態(tài))初始化區(qū)
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); //分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。
strcpy(p1, "123456"); //123456int a = 0; //全局初始化區(qū) char *p1; //全局未初始化區(qū) main() { int b; //棧 char s[] = "abc"; //棧 char *p2; //棧 char *p3 = "123456"; //123456\0在常量區(qū),p3在棧上。 static int c =0; //全局(靜態(tài))初始化區(qū) p1 = (char *)malloc(10); p2 = (char *)malloc(20); //分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。 strcpy(p1, "123456"); //123456\0放在常量區(qū),編譯器可能會(huì)將它與p3所指向的"123456"優(yōu)化成一個(gè)地方。 }放在常量區(qū),編譯器可能會(huì)將它與p3所指向的"123456"優(yōu)化成一個(gè)地方。
}
2. 存儲(chǔ)類(內(nèi)存管理):
①棧:局部變量,函數(shù)調(diào)用傳參的過程。
②堆:動(dòng)態(tài)存儲(chǔ)區(qū),需要程序員去申請(qǐng)釋放
③數(shù)據(jù)段(data段):顯式初始化僅非零的全局變量3.static修飾的變量
(1)static修飾局部變量(靜態(tài)局部變量)與普通局部變量相比:
① 靜態(tài)局部變量作用域與連接屬性,和普通局部變量一樣
② 存儲(chǔ)類:靜態(tài)局部變量分配在data/bss段,普通局部變量在棧上
③ 生命周期:因?yàn)榇鎯?chǔ)類的不同,靜態(tài)局部變量生命周期變長(zhǎng)了,直到程序結(jié)束——所以當(dāng)靜態(tài)局部變量離開作用域后,并沒有銷毀,而是仍然駐留在內(nèi)存當(dāng)中,只不過我們不能夠再對(duì)它進(jìn)行訪問,直到該函數(shù)被再次調(diào)用,并且值不變。
(2)static修飾的全局變量or函數(shù)與普通的相比:
① 存儲(chǔ)類、生命周期、作用域都一樣
② 差別在于:
static修飾的全局變量的連接屬性是內(nèi)連接,普通的是外連接
即:static修飾的全局變量不能給文件調(diào)用——這也是靜態(tài)變量和全局變量的區(qū)別。對(duì)于局部變量來說,聲明存儲(chǔ)類型的作用是指定變量存儲(chǔ)的區(qū)域(靜態(tài)存儲(chǔ)區(qū)或動(dòng)態(tài)存儲(chǔ)區(qū))以及由此產(chǎn)生的生存期的問題
對(duì)于全局變量來說,由于都是在編譯時(shí)分配內(nèi)存,都存放在靜態(tài)存儲(chǔ)區(qū),聲明存儲(chǔ)類型的作用是變量作用域的擴(kuò)展問題。
4. 其他
1. 變量類型:是對(duì)數(shù)據(jù)分配存儲(chǔ)單元的安排,包括存儲(chǔ)單元的長(zhǎng)度,及數(shù)據(jù)的存儲(chǔ)形式
2. 內(nèi)部函數(shù):只能被本文件中的其他函數(shù)調(diào)用。定義內(nèi)部函數(shù)時(shí),在函數(shù)名、函數(shù)類型前加static。
外部函數(shù):可供其他文件調(diào)用。定義外部函數(shù)時(shí),在函數(shù)首部左端加上extern。若定義函數(shù)時(shí)省略extern,則默認(rèn)為外部函數(shù)。
3. 因?yàn)锳、B、C是外部變量
所以調(diào)用max函數(shù)時(shí)用不到參數(shù)傳遞,即在max函數(shù)中可以直接使用外部變量A、B、C的值
(這一點(diǎn)與局部變量有個(gè)實(shí)參傳給形參的過程不同)
二、堆和棧有什么區(qū)別?(為什么又是這個(gè))
1、堆??臻g分配區(qū)別
棧(操作系統(tǒng)):由操作系統(tǒng)(編譯器)自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
堆(操作系統(tǒng)): 一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收,分配方式倒是類似于鏈表。
2、堆棧緩存方式區(qū)別
棧使用的是一級(jí)緩存, 它們通常都是被調(diào)用時(shí)處于存儲(chǔ)空間中,調(diào)用完畢立即釋放。
堆則是存放在二級(jí)緩存中,生命周期由虛擬機(jī)的垃圾回收算法來決定(并不是一旦成為孤兒對(duì)象就能被回收)。所以調(diào)用這些對(duì)象的速度要相對(duì)來得低一些。
3、堆棧數(shù)據(jù)結(jié)構(gòu)區(qū)別
堆(數(shù)據(jù)結(jié)構(gòu)):堆可以被看成是一棵樹,如:堆排序。
棧(數(shù)據(jù)結(jié)構(gòu)):一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu)。
三、數(shù)據(jù)結(jié)構(gòu)集中問題
1. 串值的存儲(chǔ)空間可在程序執(zhí)行過程中動(dòng)態(tài)分配而得。
2. 根結(jié)點(diǎn)是沒有雙親的,所以我們約定根結(jié)點(diǎn)的位置域?yàn)?1.
3. 鏈表翻轉(zhuǎn)(迭代法)
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* reverseList(struct ListNode* head){ if (head == NULL || head->next == NULL) return head; struct ListNode *pre = head; struct ListNode *cur = head->next; struct ListNode *tmp = head->next->next; while(cur) { tmp = cur->next; //當(dāng)前位置的下一個(gè)的值給tmp先存著 cur->next = pre; //把上一個(gè)位置的值給下一個(gè) pre = cur;//把當(dāng)前位置的值給上一個(gè) cur = tmp;//把之前下一個(gè)的值給當(dāng)前位置 } head->next = NULL; return pre; }四、指針
1. *在不同的場(chǎng)景下有不同的作用:
*可以用在指針變量的定義中,表明這是一個(gè)指針變量,以和普通變量區(qū)分開;
*也可以在使用指針變量時(shí),在變量前面加上,表示獲取指針指向的數(shù)據(jù),或者說表示的是指針指向的數(shù)據(jù)本身。也就是說,定義指針變量時(shí)的
*和使用指針變量時(shí)的*意義完全不同。以下面的語句為
int *p = &a; *p = 100;第1行代碼中
*用來指明 p 是一個(gè)指針變量,第2行代碼中*用來獲取指針指向的數(shù)據(jù)。需要注意的是,給指針變量本身賦值時(shí)不能加
*。修改上面的語句:int *p; p = &a; *p = 100;第2行代碼中的 p 前面就不能加
*。指針變量也可以出現(xiàn)在普通變量能出現(xiàn)的任何表達(dá)式中,例如:
int x, y, *px = &x, *py = &y; y = *px + 5; //表示把x的內(nèi)容加5并賦給y,*px+5相當(dāng)于(*px)+5 y = ++*px; //px的內(nèi)容加上1之后賦給y,++*px相當(dāng)于++(*px) y = *px++; //相當(dāng)于y=(*px)++ py = px; //把一個(gè)指針的值賦給另一個(gè)指針2. 如果已執(zhí)行“p=&a;”,即指針變量p指向了整型變量a,則:
printf("%d",*p);其作用是:以整數(shù)形式輸出指針變量p所指向的變量的值,即變量a的值。
3. 當(dāng)數(shù)組作為函數(shù)的參數(shù)傳遞時(shí),數(shù)組就自動(dòng)退化為同類型指針。
五、雜項(xiàng)
1. const和define的區(qū)別
1.數(shù)據(jù)類型:const修飾的變量有明確的類型,而宏沒有明確的數(shù)據(jù)類型
2.安全方面:const修飾的變量會(huì)被編譯器檢查,而宏沒有安全檢查
3.內(nèi)存分配:const修飾的變量只會(huì)在第一次賦值時(shí)分配內(nèi)存,而宏是直接替換,每次替換后的變量都會(huì)分配內(nèi)存
4.作用場(chǎng)所:const修飾的變量作用在編譯、運(yùn)行的過程中,而宏作用在預(yù)編譯中
5.代碼調(diào)試:const方便調(diào)試,而宏在預(yù)編譯中進(jìn)行所以沒有辦法進(jìn)行調(diào)試。
—— const關(guān)鍵字的作用:
1. const 是定義只讀變量的關(guān)鍵字,或者說 const 是定義常變量的關(guān)鍵字。
說 const 定義的是變量,但又相當(dāng)于常量;說它定義的是常量,但又有變量的屬性,所以叫常變量。用 const 定義常變量的方法很簡(jiǎn)單,就在通常定義變量時(shí)前面加 const 即可,如:const int a = 10;const 和變量類型 int 可以互換位置,二者是等價(jià)的,即上條語句等價(jià)于:
int const a = 10;那么用 const 修飾后和未修飾前有什么區(qū)別呢?它們不都等于 10 嗎?、
- 用 const 定義的變量的值是不允許改變的,即不允許給它重新賦值,即使是賦相同的值也不可以。所以說它定義的是只讀變量。這也就意味著必須在定義的時(shí)候就給它賦出值。
- 如果定義的時(shí)候未初始化,我們知道,對(duì)于未初始化的局部變量,程序在執(zhí)行的時(shí)候會(huì)自動(dòng)把一個(gè)很小的負(fù)數(shù)存放進(jìn)去。這樣后面再給它賦出值的話就是“改變它的值”了,即發(fā)生語法錯(cuò)誤。
2.
const int * p1 = &i; //p1指向的值不能改變
int * const p2 =&j; //p2本身的值(即指向的值的地址)不能改變
上面定義了兩個(gè)指針p1和p2。
在定義1中const限定的是*p1,即其指向空間的值不可改變,若改變其指向空間的值如*p1=20,則程序會(huì)報(bào)錯(cuò);但p1的值是可以改變的,對(duì)p1重新賦值如p1=&k是沒有任何問題的。
在定義2中const限定的是指針p2,若改變p2的值如p2=&k,程序?qū)?huì)報(bào)錯(cuò);但*p2,即其所指向的值可以改變,如*p2=80是沒有問題的,程序正常執(zhí)行。
const常量會(huì)在內(nèi)存中分配??
2. ifndef – endif 的作用:避免重定義
3. 參數(shù)傳遞:
三種參數(shù)傳遞的方式:傳值、傳指針、傳引用
形參的存儲(chǔ)空間是函數(shù)被調(diào)用時(shí)才分配的
- 引用是別名,指針是地址(實(shí)體)
引用一旦與某個(gè)對(duì)象綁定后就不再改變了
string str1 = "a";
string str3 = "b";
string &str2 = str1; //str2指向str1的地址
str2 = str3;
4. malloc和calloc
malloc:分配n個(gè)字節(jié) calloc:分配n*size個(gè)字節(jié)
5.進(jìn)程和線程的區(qū)別?
進(jìn)程:是執(zhí)行中一段程序,即一旦程序被載入到內(nèi)存中并準(zhǔn)備執(zhí)行,它就是一個(gè)進(jìn)程。進(jìn)程是表示資源分配的的基本概念,又是調(diào)度運(yùn)行的基本單位,是系統(tǒng)中的并發(fā)執(zhí)行的單位。
線程:單個(gè)進(jìn)程中執(zhí)行中每個(gè)任務(wù)就是一個(gè)線程。線程是進(jìn)程中執(zhí)行運(yùn)算的最小單位。
一個(gè)線程只能屬于一個(gè)進(jìn)程,但是一個(gè)進(jìn)程可以擁有多個(gè)線程。多線程處理就是允許一個(gè)進(jìn)程中在同一時(shí)刻執(zhí)行多個(gè)任務(wù)。
6.#include <> 和 #include” “
老生常談的問題。
#include <> :到保存系統(tǒng)標(biāo)準(zhǔn)頭文件的位置查找頭文件。
#include” “:查找當(dāng)前目錄是否有指定名稱的頭文件,然后再從標(biāo)準(zhǔn)頭文件目錄中查找。
7.遞歸
每個(gè)遞歸必須至少有一個(gè)條件,其滿足時(shí)遞歸便不再運(yùn)行,即:此時(shí)不再引用自身,而是返回值退出。
for (i=2;i<40;i++)
a[i]=a[i-1]+a[i-2];
int Fbi(int i){
return Fbi(i-1)+Fbi(i-2);
}
8. 輸入逗號(hào)
使用 scanf(“%d%d%d”,&a,&b,&c);從鍵盤中獲得任意 3 個(gè)數(shù)。在輸入數(shù)據(jù)時(shí),在兩個(gè)數(shù)據(jù)之間以一個(gè)或多個(gè)空格間隔,也可以用 Enter 健、Tab 鍵,不能用逗號(hào)作為兩個(gè)數(shù)據(jù)間的分隔符。
如果用格式輸入函數(shù) scanf(“%d,%d,%d”,&a,&b,&c) 輸入數(shù)據(jù),兩個(gè)數(shù)據(jù)之間要用“,”做間隔。
9. 各種數(shù)據(jù)類型的長(zhǎng)度
1. signed char取值范圍bai是 -128 到 127 ——一個(gè)字節(jié),2的8次方
unsigned char 取值范圍是 0 到 255
2. 一個(gè)int占4個(gè)字節(jié),sizeof就是4。在32位系統(tǒng)上,對(duì)任意指針求sizeof得到的結(jié)果都是4.??
10. 輸出
printf("x1=%7.2f\nx2=%7.2f\n",x1,x2);
x1=%7.2f
7.2是指:寬度占7個(gè),精確到小數(shù)點(diǎn)后兩位(輸出數(shù)據(jù)占7列,其中小數(shù)占2列)
x1= -1.00
x2= -2.00
NULL==ptr
free()
FreeRTOS任務(wù)調(diào)度方式
二分 哈希
pa、pb、pc 每次加 1,它們的地址分別增加 4、8、1,正好是 int、double、char 類型的長(zhǎng)度;減 2 時(shí),地址分別減少 8、16、2,正好是 int、double、char 類型長(zhǎng)度的 2 倍。
887
1.左邊界:只需要考慮相鄰右元素是否存在坑
2.右邊界:只需要考慮相鄰左元素是否存在坑
3.中間:考慮相鄰兩邊元素是否存在坑
總結(jié)
以上是生活随笔為你收集整理的C语言面试题汇总(持续更)「建议收藏」的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: code3289 花匠
- 下一篇: 最新跨年文艺句子文案朋友圈262个