首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

嵌入式 C 程式設計記憶體操作進階:指標、動態配置、關鍵字與位寬處理

     編輯:關於C語言

前言:記憶體操作是嵌入式 C 的核心競爭力

嵌入式系統選用 C 語言,99% 是為了其強大的記憶體直接操作能力。本文深入剖析資料指標、函數指標、動態記憶體策略、const/volatile 語義、CPU 與記憶體位寬不匹配處理,助你建立專業級記憶體操作功力。

一、資料指標:直接操作絕對位址

典型應用場景

  1. 記憶體映射 I/O:晶片暫存器對應特定位址
  2. 雙埠 RAM 通訊:向 Mailbox 寫入觸發對端中斷
  3. 讀取 ROM/FLASH 字模資料

基本操作


unsigned char *p = (unsigned char *)0xF000FF00;
*p = 11;  // 在絕對位址 0xF000FF00 寫入 11

指標遞增/遞減步長陷阱

宣告p++ 結果說明
unsigned char *p+11 位元組
int *p+4sizeof(int)=4
long int *p+4sizeof(long)=4 (32位系統)

鐵律:CPU 以位元組編址,C 指標以「指向型別大小」為步長。直接操作記憶體時務必宣告正確型別,或使用 unsigned char* 逐位元組存取。

二、函數指標:跳轉到任意程式碼位址

三個核心認知

  1. 函數名即函數機器碼起始位址
  2. 呼叫函數本質:PC = 函數位址 + 參數傳遞 + 回傳位址入堆疊
  3. 可「呼叫」不存在的函數實體——本質只是跳轉到該位址執行指令

經典應用:軟體復位 (80186)


typedef void (*lpFunction)(void);
lpFunction lpReset = (lpFunction)0xF000FFF0; // CPU 啟動向量
lpReset();  // 執行軟體復位

無函數本體,純粹跳轉至復位向量執行。這展示了函數指標「任意位址跳轉」的本質。

三、陣列 vs. 動態配置:嵌入式系統的取捨

動態配置風險

  • 記憶體有限,洩漏導致系統崩潰極快
  • 碎片化風險
  • 配置失敗處理複雜

錯誤模式:誰配置誰釋放原則違反


// 錯誤: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) { ... }

嵌入式鐵律

  1. 優先使用陣列:靜態配置、無碎片、無洩漏、邊界檢查可控
  2. 必須動態時:配置後立即檢查 NULL,malloc/free 嚴格成對,誰配置誰釋放

「以最笨的方式苦練神功的郭靖,勝過機智卻走錯路的楊康。」——嵌入式系統容得下笨拙,容不得錯誤。

四、關鍵字 const:唯讀契約與編譯器保護

五種宣告辨析

宣告含義
const int a;a 是唯讀整數
int const a;同上,等價
const int *a;a 是指標,指向唯讀整數(資料不可改,指標可改)
int * const a;a 是唯讀指標,指向整數(指標不可改,資料可改)
int const * const a;指標與資料皆唯讀
口訣:const 修飾右邊的實體;若左邊無實體則修飾左邊。

核心價值

  1. 傳遞設計意圖:函數參數加 const 表明「輸入參數,不修改」
  2. 編譯器強制保護:防止無意修改,減少 Bug

C 語言限制

const int SIZE = 10; char a[SIZE]; ❌ 非法。C 編譯期常數只能用 #define。const 本質是「不能改的變數」,非真常數。

五、關鍵字 volatile:阻止編譯器過度優化

問題場景


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;  // 告訴編譯器:每次存取必須真實發生

適用場景

  1. 平行裝置硬體暫存器(狀態暫存器、資料暫存器)
  2. 中斷服務程式存取的非自動變數(全域變數)
  3. 多執行緒共享變數

六、CPU 字長與記憶體位寬不匹配處理

案例:80186 (16-bit) + NVRAM (8-bit)

地址線連接:CPU A1 接 NVRAM A0,CPU A0 懸空。NVRAM 只能佔用偶數位址,每前進一個邏輯單元,位址增 2 (0x10)。

CPU與NVRAM地址線連接圖

介面函數實作


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,請愛指標;愛指標,請愛指標的指標!

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved