據(jù)結構專項完整版))
適用場景嵌入式校招/社招筆試、面試核心考點聚焦C語言底層、嵌入式常用數(shù)據(jù)結構純高頻必考內(nèi)容核心覆蓋C語言關鍵字、指針、內(nèi)存、編譯特性、中斷規(guī)范、手撕代碼、鏈表、棧隊列、字符串算法一、C語言核心嵌入式必考重中之重1. 三大關鍵字const / static / volatileconst 常量修飾const int a變量a只讀程序運行期間不可修改const int *p指針指向的內(nèi)存內(nèi)容不可改指針變量本身地址可修改int *const p指針本身存儲的地址不可改指向的內(nèi)存內(nèi)容可修改const int *const p指針地址、指向的內(nèi)存內(nèi)容均不可修改代碼示例解析// 1. const修飾普通變量只讀 const int a 10; // a 20; // 編譯報錯常量不可修改 // 2. const修飾指針指向內(nèi)容只讀指針可移動 int b 100; const int *p1 b; // *p1 200; // 編譯報錯無法修改指向內(nèi)容 p1 NULL; // 合法指針本身地址可修改 // 3. const修飾指針變量地址只讀內(nèi)容可改 int *const p2 b; *p2 200; // 合法可修改內(nèi)容 // p2 NULL; // 編譯報錯指針地址固定不可改 // 4. 雙重const內(nèi)容、地址全部只讀 const int *const p3 b; // *p3 300; // 報錯 // p3 NULL; // 報錯嵌入式特性全局const常量存儲在Flash中不占用寶貴的RAM內(nèi)存可有效節(jié)省運行內(nèi)存資源。static 靜態(tài)修飾局部static變量生命周期貫穿整個程序運行周期僅在首次執(zhí)行時初始化一次存儲在全局數(shù)據(jù)段函數(shù)調(diào)用結束后變量值不銷毀、保留原值全局/函數(shù)static作用域嚴格僅限當前.c文件禁止外部文件通過extern關鍵字引用實現(xiàn)代碼私有化、模塊化隔離避免全局變量沖突代碼示例解析局部staticvoid test_static(void) { int num1 0; // 局部變量每次調(diào)用重新初始化 static int num2 0; // static局部變量僅初始化一次常駐內(nèi)存 num1; num2; printf(num1%d, num2%d\n, num1, num2); } // 連續(xù)調(diào)用三次 // 第一次num11, num21 // 第二次num11, num22 // 第三次num11, num23 // 結論普通局部變量棧釋放即清零static變量值永久保留static全局特性說明static全局變量/函數(shù)僅本文件可見有效防止多文件全局變量重定義沖突是嵌入式模塊化開發(fā)常用手段。volatile 易變變量嵌入式最高頻核心作用禁止編譯器對該變量進行優(yōu)化每次讀取變量都從原始物理內(nèi)存/硬件寄存器讀取不使用CPU寄存器緩存的舊值。必用場景硬件外設寄存器映射變量中斷服務函數(shù)中修改、主循環(huán)讀取的全局變量多RTOS任務、多線程共享的全局變量核心注意volatile 僅保證內(nèi)存讀取實時性不保證操作原子性無法替代互斥鎖、臨界區(qū)保護機制。嵌入式代碼示例解析// 未加volatile編譯器優(yōu)化只讀一次緩存值中斷修改后主循環(huán)感知不到 // 加volatile強制每次讀內(nèi)存最新值 volatile int g_flag 0; // 中斷服務函數(shù) void EXTI_IRQHandler(void) { g_flag 1; // 中斷修改全局標志 } // 主循環(huán) while(1) { if(g_flag 1) { // 處理任務 g_flag 0; } }面試考點無volatile時編譯器會優(yōu)化將g_flag緩存到CPU寄存器中斷修改內(nèi)存值后主循環(huán)永遠讀取舊值導致程序卡死。2. 指針與數(shù)組核心區(qū)別嵌入式高頻坑點char a[]數(shù)組是連續(xù)內(nèi)存實體變量分配獨立內(nèi)存空間sizeof(a)可獲取數(shù)組總字節(jié)大小char *p指針是存儲內(nèi)存地址的變量本身占用極小空間sizeof(p)恒為4字節(jié)32位系統(tǒng)/8字節(jié)64位系統(tǒng)與指向內(nèi)容無關strlen僅統(tǒng)計字符串有效字符個數(shù)遇\0終止不包含結束符無法統(tǒng)計無結束符的字符數(shù)組野指針未初始化、指向已釋放內(nèi)存的指針指向隨機非法地址直接導致程序死機、硬件訪問異??罩羔楴ULL本質(zhì)是宏定義(void*)0指向內(nèi)存0地址屬于非法訪問地址絕對禁止解引用操作對比代碼示例解析char str[] 12345; char *p 12345; printf(sizeof(str)%d\n, sizeof(str)); // 6含\0數(shù)組總大小 printf(sizeof(p)%d\n, sizeof(p)); // 4/8僅指針地址大小 printf(strlen(str)%d\n, strlen(str));// 5不計\0 // 關鍵區(qū)別數(shù)組可寫字符串常量指針不可寫 str[0] 9; // 合法數(shù)組在棧區(qū)可修改 // p[0] 9; // 崩潰指向rodata只讀段3. 程序六大內(nèi)存分區(qū)嵌入式底層核心MCU裸機/Linux嵌入式程序運行時完整內(nèi)存分布內(nèi)存分區(qū)存儲內(nèi)容讀寫屬性存儲介質(zhì)掉電是否丟失嵌入式特性.text 代碼段程序可執(zhí)行代碼、函數(shù)體只讀Flash/ROM不丟失固化存儲程序運行基礎.rodata 只讀常量段const全局常量、字符串常量只讀Flash/ROM不丟失節(jié)省RAM嵌入式常用.data 初始化數(shù)據(jù)段已初始化的全局變量、static變量可讀可寫RAM丟失占用RAM需初始化值.bss 未初始化數(shù)據(jù)段未初始化的全局變量、static變量可讀可寫RAM丟失啟動時自動清零節(jié)省代碼空間stack 棧區(qū)局部變量、函數(shù)參數(shù)、返回地址可讀可寫RAM丟失空間小自動分配釋放遞歸過深易溢出heap 堆區(qū)malloc/free動態(tài)申請內(nèi)存可讀可寫RAM丟失需手動管理易產(chǎn)生碎片嵌入式慎用核心對比要點Flash存儲.text和.rodata段固化在Flash不占用RAM適合嵌入式資源受限場景RAM存儲.data、.bss、stack、heap占用RAM掉電丟失需合理規(guī)劃大小自動管理stack由系統(tǒng)自動分配釋放.data/.bss由鏈接器自動布局手動管理heap需程序員手動申請/釋放易產(chǎn)生內(nèi)存泄漏和碎片嵌入式優(yōu)化多用const節(jié)省RAM慎用動態(tài)內(nèi)存優(yōu)先靜態(tài)分配4. malloc/free 常見坑點與嵌入式規(guī)范malloc申請的堆內(nèi)存不會自動清零臟數(shù)據(jù)隨機使用前必須手動memset初始化內(nèi)存泄漏僅malloc申請內(nèi)存無free釋放長期運行導致RAM耗盡、程序死機重啟非法釋放重復free、free棧內(nèi)存/常量內(nèi)存直接觸發(fā)程序崩潰內(nèi)存越界讀寫超出申請的堆內(nèi)存空間破壞堆內(nèi)存管理塊導致后續(xù)malloc/free異常、程序隨機死機嵌入式RTOS開發(fā)規(guī)范盡量避免頻繁動態(tài)malloc優(yōu)先使用全局靜態(tài)數(shù)組、靜態(tài)內(nèi)存池標準規(guī)范代碼示例#include stdlib.h #include string.h void mem_test(void) { // 1. 申請內(nèi)存 手動清零嵌入式標準寫法 char *buf (char *)malloc(128); if(buf NULL) return; // 必須判空防止申請失敗 memset(buf, 0, 128); // 業(yè)務操作... // 2. 使用完畢必須釋放 free(buf); buf NULL; // 置空防止野指針、重復free }5. 結構體對齊與位域硬件驅動必備內(nèi)存對齊核心規(guī)則結構體每個成員的偏移地址必須是自身數(shù)據(jù)類型大小的整數(shù)倍結構體整體總大小必須是內(nèi)部最大基礎成員大小的整數(shù)倍#pragma pack(n)可手動指定對齊字節(jié)常用于適配硬件寄存器、協(xié)議幀結構體對齊代碼演示// 默認對齊 struct Test1{ char a; // 1字節(jié)偏移0補3字節(jié)對齊 int b; // 4字節(jié)偏移4 }; // 總大小8字節(jié) // 1字節(jié)緊湊對齊協(xié)議解析專用 #pragma pack(1) struct Test2{ char a; int b; }; // 總大小5字節(jié)無冗余填充 #pragma pack() // 恢復默認對齊位域定義格式unsigned int val:2;精準控制變量占用bit位數(shù)核心用于硬件寄存器配置、通信協(xié)議幀解析極大節(jié)省內(nèi)存空間缺點是平臺可移植性較差不同編譯器可能存在差異。位域代碼示例寄存器模擬// 模擬硬件狀態(tài)寄存器按位分配功能 struct REG_STATUS{ unsigned int enable : 1; // 1bit設備使能 unsigned int mode : 2; // 2bit工作模式 unsigned int err : 1; // 1bit錯誤標志 unsigned int reserve: 28; // 保留位 }; // 僅占用4字節(jié)精準匹配硬件寄存器位定義6. 宏定義 #define 核心要點與規(guī)范宏是預處理階段純文本替換無類型檢查、無函數(shù)調(diào)用開銷執(zhí)行效率極高宏參數(shù)必須全員加括號避免運算符優(yōu)先級出錯標準示例#define MAX(a,b) ((a)(b)?(a):(b))多行宏必須搭配do{...}while(0)兼容分號結尾、保證代碼塊完整性宏代碼避坑示例// 錯誤寫法無括號優(yōu)先級出錯 #define BAD_MAX(a,b) ab?a:b // BAD_MAX(12,3) 會展開為 123?12:3 邏輯異常 // 標準正確寫法 #define MAX(a,b) ((a)(b)?(a):(b)) // 多行宏標準寫法嵌入式日志常用 #define PRINT_ERR(msg) do{ \ printf(ERR:%s\r\n, msg); \ }while(0)7. 大小端原理 手撕判斷代碼原理ARM/STM32/x86為小端模式低字節(jié)存低地址網(wǎng)絡、51單片機為大端模式高字節(jié)存低地址// 手撕大小端判斷函數(shù)面試必考 int check_endian(void) { union{ int a; char b; }u; u.a 0x12345678; // 低地址存低位0x78 小端 return (u.b 0x78) ? 1 : 0; }二、嵌入式高頻數(shù)據(jù)結構極簡面試版1. 單鏈表嵌入式最常用適用場景設備節(jié)點管理、緩沖區(qū)鏈表、狀態(tài)隊列// 鏈表節(jié)點結構體 typedef struct Node{ int data; struct Node *next; }Node; // 鏈表頭插法 void list_head_insert(Node **head, int val) { Node *new_node (Node *)malloc(sizeof(Node)); new_node-data val; new_node-next *head; *head new_node; }2. 棧與隊列RTOS底層核心棧后進先出LIFO用途函數(shù)調(diào)用棧、表達式解析、臨時緩存隊列先進先出FIFO用途串口接收隊列、消息緩存、任務消息隊列RTOS消息隊列底層原理3. 面試手撕高頻算法嵌入式必考字符串反轉void str_reverse(char *str) { int i 0, j strlen(str)-1; while(i j) { char tmp str[i]; str[i] str[j]; str[j] tmp; i; j--; } }手寫strlen不調(diào)用庫函數(shù)int my_strlen(char *str) { int len 0; while(str[len] ! \0) len; return len; }