嵌入式開發頻繁面對 16/32/64 位編譯器,基本類型尺寸差異直接影響內存佈局、通信協議、寄存器操作。下表為核心類型在主流架構下的字節數對照:
| 類型 | 16位 | 32位 | 64位 | 備註 |
|---|---|---|---|---|
char | 1 | 1 | 1 | 標準保證 |
char* (指針) | 2 | 4 | 8 | 等於地址總線寬度/8 |
short | 2 | 2 | 2 | 通常 16 位 |
int | 2 | 4 | 4 | 自然字長 |
long | 4 | 4 | 8 | LP64 模型下 64 位為 8 |
long long | 8 | 8 | 8 | C99 引入,保證 ≥64 位 |
float | 4 | 4 | 4 | IEEE 754 單精度 |
double | 8 | 8 | 8 | IEEE 754 雙精度 |
最佳實踐:使用 <stdint.h> 定義的精確寬度類型(int32_t、uintptr_t 等),消除平台差異;結構體傳輸前必須處理字節序(大端/小端轉換)。
嵌入式代碼頻現複雜指針聲明,掌握「右左法則」可秒懂:
int (*a)(int); → a 是指針 → 指向函數 → 參數 int → 返回 intint (*a[10])(int); → a 是數組[10] → 元素是指針 → 指向函數 → 參數/返回同前讀取順序:從變量名起始,向右找運算符,再向左,遇到括號優先處理括號內。
const 與 volatile:嵌入式雙子星const:只讀語義的多重含義const int a; // a 是常數 int const a; // 同上,風格差異 const int *p; // p 指向常數,數據不可改,指針可改 int *const p; // p 是常指針,指針不可改,數據可改 int const *const p; // 常指針指向常數,雙重鎖死
嵌入式典型場景:
volatile const uint32_t *STATUS_REGconst char FIRMWARE_VER[] = "v1.2.3"void process(const Config *cfg)volatile:告訴編譯器「別優化我」適用於:
經典反例:
// 錯誤:編譯器可能優化為讀一次、平方
int square(volatile int *ptr) { return *ptr * *ptr; }
// 正確:強制兩次讀取
int square(volatile int *ptr) {
int val = *ptr; // 第一次讀取
return val * val; // 使用副本
}
volatile 禁止編譯器對該變量的讀寫重排、合併、缓存優化,確保每次訪問真實發生於總線週期。
寄存器級編程的核心技能,標準模式:
#define BIT(n) (1U << (n)) // 無符號防符號擴展陷阱
static uint32_t reg = 0;
void set_bit3() { reg |= BIT(3); } // 置位
void clear_bit3() { reg &= ~BIT(3); } // 清零
void toggle_bit3() { reg ^= BIT(3); } // 翻轉
bool test_bit3() { return reg & BIT(3); } // 測試
關鍵點:
1U 避免左移進入符號位導致未定義行為// 向 0x67A9 寫入 0xAA55 #define HW_REG (*(volatile uint16_t *)0x67A9) HW_REG = 0xAA55; // 宏封裝提升可讀性 #define REG_ADDR(base, offset) (*(volatile uint32_t *)((base) + (offset))) REG_ADDR(0x40000000, 0x14) = 0x1; // GPIO 輸出置高
volatile 必不可少,防止編譯器優化掉「看似無用」的寫入。
<stdarg.h> 機制深度剖析printf 等函數的可變參數實現依賴棧幀佈局與宏操作:
#include <stdarg.h>
void log_msg(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt); // 1. 初始化,定位到第一個可變參數
vprintf(fmt, ap); // 2. 使用 vprintf 族函數處理
va_end(ap); // 3. 清理
}
va_start(ap, last_fixed) 讓 ap 指向最後一個固定參數之後的棧地址va_arg(ap, type) 根據類型大小遞增 ap,返回舊地址的值va_end 置 ap 為 NULLva_arg 完全信任程序員指定類型,錯誤導致棧破壞。printf 通過格式串解析類型實現「偽類型安全」NULL 結尾)、計數參數或格式串隱含計數float → double、char/short → int,va_arg 必須匹配提升後類型va_list 可能是結構體而非指針,絕不可直接操作 va_list 內部結構volatilestdint.h 精確寬度類型#pragma pack / __attribute__((packed)),注意對齊陷阱vxxx 形式,對外提供定參接口-Wall -Wextra -Wpedantic),視警告為錯誤