嵌入式系統選用 C 語言,99% 是為了其強大的記憶體直接操作能力。本文深入剖析資料指標、函數指標、動態記憶體策略、const/volatile 語義、CPU 與記憶體位寬不匹配處理,助你建立專業級記憶體操作功力。
unsigned char *p = (unsigned char *)0xF000FF00;
*p = 11; // 在絕對位址 0xF000FF00 寫入 11
| 宣告 | p++ 結果 | 說明 |
|---|---|---|
unsigned char *p | +1 | 1 位元組 |
int *p | +4 | sizeof(int)=4 |
long int *p | +4 | sizeof(long)=4 (32位系統) |
鐵律:CPU 以位元組編址,C 指標以「指向型別大小」為步長。直接操作記憶體時務必宣告正確型別,或使用 unsigned char* 逐位元組存取。
PC = 函數位址 + 參數傳遞 + 回傳位址入堆疊
typedef void (*lpFunction)(void);
lpFunction lpReset = (lpFunction)0xF000FFF0; // CPU 啟動向量
lpReset(); // 執行軟體復位
無函數本體,純粹跳轉至復位向量執行。這展示了函數指標「任意位址跳轉」的本質。
// 錯誤:function 內部 malloc,呼叫端 free
char *function() {
char *p = malloc(...);
return p;
}
char *q = function(); free(q); // 耦合度高,呼叫端需知內部細節
char *p = malloc(...);
if (p) {
function(p); // 傳入緩衝區
free(p);
p = NULL;
}
void function(char *p) { ... }
malloc/free 嚴格成對,誰配置誰釋放「以最笨的方式苦練神功的郭靖,勝過機智卻走錯路的楊康。」——嵌入式系統容得下笨拙,容不得錯誤。
| 宣告 | 含義 |
|---|---|
const int a; | a 是唯讀整數 |
int const a; | 同上,等價 |
const int *a; | a 是指標,指向唯讀整數(資料不可改,指標可改) |
int * const a; | a 是唯讀指標,指向整數(指標不可改,資料可改) |
int const * const a; | 指標與資料皆唯讀 |
口訣:const 修飾右邊的實體;若左邊無實體則修飾左邊。
const 表明「輸入參數,不修改」const int SIZE = 10; char a[SIZE]; ❌ 非法。C 編譯期常數只能用 #define。const 本質是「不能改的變數」,非真常數。
int a, b, c;
a = inWord(0x100); // 讀取 I/O 埠
b = a;
a = inWord(0x100); // 再次讀取
c = a;
編譯器可能優化為:
a = inWord(0x100);
b = a;
c = a; // 省略第二次讀取!
若硬體暫存器值在兩次讀取間改變,b、c 將錯誤相同。
volatile int a; // 告訴編譯器:每次存取必須真實發生
地址線連接:CPU A1 接 NVRAM A0,CPU A0 懸空。NVRAM 只能佔用偶數位址,每前進一個邏輯單元,位址增 2 (0x10)。
typedef unsigned char BYTE;
typedef unsigned int WORD;
extern BYTE ReadByteNVRAM(WORD wOffset) {
LPBYTE lpAddr = (BYTE*)(NVRAM_BASE + wOffset * 2);
return *lpAddr;
}
extern WORD ReadWordNVRAM(WORD wOffset) {
WORD wTmp = 0;
LPBYTE lpAddr = (BYTE*)(NVRAM_BASE + wOffset * 2);
wTmp += (*lpAddr) * 256; // 高位元組
lpAddr = (BYTE*)(NVRAM_BASE + (wOffset + 1) * 2);
wTmp += *lpAddr; // 低位元組
return wTmp;
}
// WriteByteNVRAM / WriteWordNVRAM 同理
精通資料指標絕對位址操作、函數指標任意跳轉、動態記憶體紀律、const/volatile 語義、異位寬記憶體介面設計,即掌握 C 語言 99% 精華。若你愛編程,請愛 C;愛 C,請愛指標;愛指標,請愛指標的指標!