文章目錄
- 1. C語言基礎
- 1.1 const
- 1.2 static
- 1.3 回調函數的用法
- 1.4 宏定義
- 1.5 編譯、鏈接過程
- 1.6 堆與棧的區別?
- 1.7 簡單的字符串算法題,C語言實現
- 1.7.1 給定一個字符串,按順序篩選出不重復的字符組成字符串,輸出該字符串
- 1.7.2 給定4*4矩陣,回文打印輸出
- 1.8 字節對其
- 2. MCU相關
- 2.1 MCU的啟動過程描述
- 2.2 MCU的內存布局
- 2.3 使用volatile關鍵字的作用?
- 3. 汽車電子軟件
- 3.1 CAN/CANFD相關
- 3.1 概述一個CAN消息如何被發送和接收的
- 3.2 CAN和FIFO CAN
- 3.3 CANFD的知識點
- 3.2 概述bootloader實現要點
- 3.2.1 跳轉前要做什么?
- 3.3 簡述ASPICE在項目研發中的應用
- 3.4 舉例說明某個功能安全需求的實現過程
- 3.5 概述14229協議
- 3.6 概述15765協議
1. C語言基礎
1.1 const
修飾變量
只可訪問,不可重新賦值。
const int MAX_VALUE = 100;void printValue(const int value);
修飾指針
- 限制指向位置
const int *ptr;
- 限制指向數據
const int *const ptr;
1.2 static
靜態變量
使用 static 關鍵字聲明靜態變量時,變量的生命周期會延長到整個程序運行期間,而不僅僅局限在其定義的作用域內。靜態變量在第一次被賦值時初始化,并且保留其值直到程序結束。
靜態全局變量
使用 static 關鍵字在全局作用域中聲明的變量具有靜態存儲持續時間,但是其作用域被限制在聲明該變量的源文件內。這使得該變量對其他源文件不可見,可以防止命名沖突。
靜態函數
使用 static 關鍵字聲明靜態函數時,該函數僅在聲明所在的源文件中可見,即它具有內部鏈接性。靜態函數的作用域僅限于聲明所在的源文件。這種方式可以避免與其他源文件中的同名函數產生沖突。
1.3 回調函數的用法
用于在函數執行過程中調用另一個函數。回調函數允許我們向一個函數傳遞另一個函數的地址,從而在需要時執行特定的操作。回調函數通常用于事件處理、異步編程、庫函數的擴展等場景。
- 定義回調函數
首先定義一個函數作為回調函數,其函數原型應與回調的要求相匹配。例如:
void callbackFunction(int result) {printf("Callback result: %d\n", result);
}
- 在函數中注冊回調函數
在需要的地方將回調函數注冊進目標函數中,通常通過函數指針實現。例如:
void performOperation(void (*callback)(int)) {int result = 100; // 模擬操作結果// 執行操作...// 調用回調函數callback(result);
}
- 調用包含回調函數的函數
最后調用包含回調函數的函數,將回調函數的地址傳遞給要調用的函數。例如:
int main() {performOperation(callbackFunction); // 注冊回調函數return 0;
}
在這個示例中,performOperation 函數執行某個操作后調用了注冊的回調函數 callbackFunction,并將結果傳遞給回調函數進行處理。
通過回調函數,我們可以實現靈活的程序設計,允許函數根據不同情況來調用不同的操作,增加了程序的可擴展性和可重用性。當需要在函數執行過程中動態切換功能時,回調函數是一個非常有用的工具。
使用回調函數有以下一些好處:
- 靈活性和可擴展性
回調函數提供了一種靈活的機制,使得代碼可以在不同的場景中進行定制和擴展。通過將特定的功能封裝在回調函數中,可以根據需要動態地更改或添加行為,而無需修改主函數的邏輯。 - 解耦和模塊化
回調函數有助于將不同的功能模塊分離,使代碼更具有模塊化和可維護性。主函數可以專注于其核心邏輯,而將特定的任務委托給回調函數來處理。這樣可以提高代碼的可讀性和可重用性。 - 異步處理和事件驅動
回調函數常用于異步操作或事件驅動的場景中。例如,在異步 I/O 操作完成或特定事件發生時,可以通過回調函數來處理相應的邏輯。這有助于提高程序的并發性和響應性。 - 定制性和擴展性
回調函數允許用戶提供自己的自定義邏輯,以滿足特定的需求。這使得程序可以更好地適應各種不同的用例和業務邏輯。 - 代碼復用
回調函數可以作為可復用的模塊,在多個地方被調用,從而減少代碼冗余。
需要注意的是,在使用回調函數時,要確保正確處理回調函數的參數和返回值,并注意線程安全等問題。合理使用回調函數可以提高代碼的靈活性和擴展性,但也需要謹慎設計和管理,以避免引入復雜度過高或難以調試的問題。
1.4 宏定義
在 C 語言中,宏定義是一種預處理器指令,用于在編譯階段進行文本替換。它允許你定義一個標識符(通常是一個宏名),并將其與一個特定的文本表達式或代碼塊關聯起來。當在代碼中使用該宏名時,編譯器會將其替換為相應的文本。
宏定義的常見用法和好處包括:
- 常量定義
使用宏定義可以創建常量,例如定義一些具有特定值的常量,以增強代碼的可讀性和可維護性。 - 代碼簡化和抽象
宏定義可以用于簡化復雜的表達式或代碼塊,使其更易于閱讀和理解。例如,將常用的計算或操作封裝在宏中,以便在多個地方重復使用。 - 條件編譯
通過宏定義可以實現條件編譯,根據不同的條件編譯不同的代碼塊。這對于處理不同平臺、版本或配置的情況非常有用。 - 代碼移植性
宏定義可以幫助提高代碼的可移植性。例如,可以使用宏來定義平臺特定的代碼或處理不同編譯器的差異。 - 提高性能
在一些情況下,宏定義可以提供一定的性能優勢,特別是對于一些簡單的計算或操作。
例如,以下是一個簡單的宏定義示例:
#define MAX_SIZE 100
在上面的示例中,MAX_SIZE 是一個宏名,100 是它關聯的文本。在代碼中使用 MAX_SIZE 時,它將被替換為 100。
需要注意的是,宏定義也有一些潛在的問題和限制:
- 宏展開問題
宏在編譯時會進行文本替換,可能會導致一些意外的副作用,例如嵌套宏展開、參數求值順序等問題。 - 缺乏類型檢查
宏不進行類型檢查,可能會導致在使用時出現類型不匹配或其他錯誤。 - 可讀性問題
過度使用宏可能會使代碼變得難以理解,特別是當宏的定義和使用變得復雜時。
因此,在使用宏定義時,應該謹慎考慮,并確保其使用不會導致代碼的可讀性和可維護性下降。在一些情況下,使用函數或其他語言特性可能是更好的選擇。
1.5 編譯、鏈接過程
預處理
根據以字符#開頭的命令修飾的main.c的C源文件,生成預處理后的C源文件 main.i。
該過程主要進行文本替換、宏展開、刪除注釋等工作。
對應的gcc命令:
gcc -E main.c main.i
編譯
編譯器將文本文件main.i翻譯(編譯)成匯編文件main.s
對應的gcc命令:
gcc -S main.i mian.s
匯編
編譯器將main.s翻譯成機器語言指令,并把這些指令打包成一種可重定位目標程序的格式,并將結果保存在目標文件main.o中
把一個源文件翻譯成目標程序的工作過程分為五個階段:詞法分析、語法分析、語義檢查和中間代碼生成。主要是進行詞法分析和語法分析,又稱為源程序分析,分析過程中發現語法錯誤并給出提示信息。
對應的gcc命令:
gcc -c main.s mian.o
鏈接
該過程編譯器將靜態庫和動態庫的庫函數鏈接到可執行程序中。
靜態庫是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時就不在需要庫文件了,其后綴一般為.a。
動態庫則是在程序運行時被鏈接加載,這樣可以節省系統的開銷,其后綴一般為.so,gcc在編譯時默認使用動態庫。
1.6 堆與棧的區別?
- 棧空間是系統自動分配和回收,堆的空間是用戶手動分配回收的;
- 棧空間較小,堆空間較大;
- 棧的地址空間向下生長,堆則向上生長;
- 棧的存儲效率更高。
參考:棧和堆,以STM32為例說明
1.7 簡單的字符串算法題,C語言實現
1.7.1 給定一個字符串,按順序篩選出不重復的字符組成字符串,輸出該字符串
參考示例:
#include <stdio.h>
#include <string.h>void removeDuplicates(char *str) {int len = strlen(str);if (len < 2) return;int tail = 1;for (int i = 1; i < len; ++i) {int j;for (j = 0; j < tail; ++j) {if (str[i] == str[j]) break;}if (j == tail) {str[tail] = str[i];++tail;}}str[tail] = '\0'; //此處是關鍵
}int main() {char str[100];printf("Enter a string: ");scanf("%s", str);removeDuplicates(str);printf("String with duplicates removed: %s\n", str);return 0;
}
測試結果:
Enter a string: asbdssjikSNjs78137!@#ssa00smk
String with duplicates removed: asbdjikSN7813!@#0m
1.7.2 給定4*4矩陣,回文打印輸出
參考示例:
#include <stdio.h>#define ROWS 4
#define COLS 4void printClockwise(int matrix[ROWS][COLS]) {int top = 0, bottom = ROWS - 1, left = 0, right = COLS - 1;while (top <= bottom && left <= right) {// Print top rowfor (int i = left; i <= right; ++i)printf("%d ", matrix[top][i]);top++;// Print right columnfor (int i = top; i <= bottom; ++i)printf("%d ", matrix[i][right]);right--;// Print bottom rowif (top <= bottom) {for (int i = right; i >= left; --i)printf("%d ", matrix[bottom][i]);bottom--;}// Print left columnif (left <= right) {for (int i = bottom; i >= top; --i)printf("%d ", matrix[i][left]);left++;}}
}int main() {int matrix[ROWS][COLS] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12},{13, 14, 15, 16}};printf("Clockwise printing of the matrix:\n");printClockwise(matrix);return 0;
}
測試結果:
Clockwise printing of the matrix:
1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10
參考:算法11:順時針轉圈打印矩陣
1.8 字節對其
【問題】32位系統,一個結構體中,成員依次是char、short、int、char類型,問這個結構體總共占多少字節?
回答這個問題需要深刻理解結構體所占空間的分布:
|char |-----|short|short|4字節
|int |int |int |int |4字節
|char |-----|-----|-----|4字節
所以,該結構體共占12字節
測試代碼:
#include <stdio.h>struct TMP{char a;short b;int c;char d;
};int main(void) {printf("size = %d", sizeof(struct TMP));return 0;
}
總結:
- 結構體成員占位是其自身類型長度的整數倍
- 結構體整體需要對齊,目標對齊長度的整數倍
2. MCU相關
2.1 MCU的啟動過程描述
參考STM32的啟動過程 — startup_xxxx.s文件解析(MDK和GCC雙環境)
2.2 MCU的內存布局
參考:
- 內存布局:深度剖析應用程序中的內存布局
- stm32的內存分布
2.3 使用volatile關鍵字的作用?
- 硬件寄存器操作
單片機通常與硬件設備交互,硬件寄存器的值可能會在硬件事件的觸發下發生改變。通過將訪問硬件寄存器的變量聲明為 volatile,可以告訴編譯器不要對該變量進行優化,以確保每次訪問都能獲取到最新的寄存器值。 - 共享變量
在多線程或中斷處理程序中,多個執行路徑可能同時訪問和修改同一個變量。將這樣的共享變量聲明為 volatile,可以確保編譯器生成的代碼正確地處理變量的讀和寫,避免出現競態條件等問題。 - 中斷服務程序
中斷服務程序可能會修改一些全局變量,而這些變量在主程序中也會被訪問。將這些變量聲明為 volatile,可以保證中斷服務程序對變量的修改能及時反映到主程序中。 - 實時性要求高的代碼
在一些對實時性要求較高的場景中,使用 volatile 可以確保關鍵變量的訪問不會被編譯器優化掉,從而保證代碼的實時性。
通過使用 volatile,可以幫助編譯器生成更準確的代碼,避免一些由于變量的不確定性導致的問題。然而,具體的應用場景和使用方法可能會因單片機的類型、編譯器的特性以及項目的需求而有所不同。在實際編程中,還需要根據具體情況進行適當的測試和調試。
3. 汽車電子軟件
3.1 CAN/CANFD相關
3.1 概述一個CAN消息如何被發送和接收的
TBD.
3.2 CAN和FIFO CAN
TBD.
3.3 CANFD的知識點
TBD.
3.2 概述bootloader實現要點
3.2.1 跳轉前要做什么?
- 禁止所有外設時鐘;
- 禁止使用的 PLL;
- 禁止所有中斷;
- 清除所有中斷掛起標志。
3.3 簡述ASPICE在項目研發中的應用
TBD.
3.4 舉例說明某個功能安全需求的實現過程
TBD.
3.5 概述14229協議
TBD.
3.6 概述15765協議
TBD.