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