在 Windows Native 開發(驅動程式、系統服務、核心模組)中,C 語言函數機制是基石。本文系統整理函數聲明/定義規範、參數傳遞機制、遞迴實作、變數儲存類別與可變參數處理,並針對內核開發常見陷阱給出最佳實踐。
// cFunction.h
#ifndef _MYHEAD_FUNCTION_
#define _MYHEAD_FUNCTION_
#ifdef __cplusplus
extern "C" // 防止 C++ 名稱修飾,確保 C 連結語意
#endif
// 標準聲明:供外部模組調用
char *demo_cFunction(void);
long recursion(int n);
void update(int ary[], int aryLength);
// 函數原型:僅保留型別,參數名可省略
void update(int[], int);
// 內部函數:static 限制連結可見性於本編譯單元
static void cFunction_demo1(void);
// 外部函數:extern 明確標示(預設即為 extern)
extern void cFunction_demo2(void);
void cFunction_demo3(void); // 隱含 extern
#endif
// cFunction.c
#include "pch.h"
#include "cFunction.h"
#include "cHelper.h"
// 全域變數:靜態儲存期,整個程式存續期
int global01 = 100;
// 本檔案內部函數:無需外部聲明即可調用
void function0(void) { }
// 函數定義
char *demo_cFunction(void) {
function0(); // 同檔案前向調用無需聲明
// ... 實作細節
}
C 語言**僅支援值傳遞**。形參是實參的副本,生命週期僅限函數執行期間。
int function2(int a, int b) { // a, b 為形參
return a + b;
}
// 調用端
int x = function2(1, 2); // 1, 2 為實參
void update(int ary[], int len) { // 等價於 int *ary
for (int i = 0; i < len; i++) ary[i] = 0; // 修改影響原陣列
}
// 調用
int arr[] = {0,1,2,3,4};
update(arr, 5); // arr 退化為指標,實際傳遞首元素位址
注意:陣列長度資訊**不會**傳遞給函數,必須額外傳遞 len 參數。
// 計算 1+2+...+n
long recursion(int n) {
long result = 0;
if (n > 0) {
result += n;
result += recursion(n - 1); // 遞迴呼叫
}
return result;
}
內核開發中**慎用遞迴**:棧空間有限(通常 12KB~24KB),深度遞迴易導致棧溢位。優先考慮迴圈改寫或尾遞迴優化(需編譯器支援)。
| 關鍵字 | 儲存期 | 作用域 | 初始值 | 典型用途 |
|---|---|---|---|---|
| (無)/auto | 動態(棧) | 區塊 | 不定 | 區域變數 |
| static | 靜態 | 區塊/檔案 | 0 | 保持狀態、檔案內部函數 |
| register | 動態(暫存器/棧) | 區塊 | 不定 | 熱點迴圈計數器(建議由編譯器決定) |
| volatile | 動態/靜態 | 依宣告位置 | 不定 | 硬體暫存器、訊號處理、多執行緒共享變數 |
| extern | 靜態 | 全域 | 0 | 跨檔案共享全域變數/函數 |
void demo_variable2(void) {
int i = 0; // auto,動態儲存
static int j = 0; // 靜態區域變數,跨呼叫保持值
register int x = 0; // 建議放入暫存器,不可取址 &
volatile int y = 0; // 強制記憶體存取,防編譯器優化
extern int global02; // 引用他處定義的全域變數
extern int global03;
int p02 = global02; // 使用外部變數
}
int global02 = 200, global03 = 300; // 定義點在使用點之後
使用 <stdarg.h> 機制:
#include <stdarg.h>
float average(int count, ...) {
va_list ap;
va_start(ap, count); // 初始化,count 為最後一個固定參數
long sum = 0;
for (int i = 0; i < count; i++)
sum += va_arg(ap, int); // 依序取出參數,需指定型別
va_end(ap); // 清理
return (float)sum / count;
}
// 調用
float avg = average(4, 1, 2, 3, 4); // 2.5
內核開發注意:va_list 在 x64 呼叫慣例中為結構體,傳遞時需傳指標或使用 va_copy;嚴禁在中斷上下文(IRQL > PASSIVE_LEVEL)使用浮點/可變參數。
extern "C" 防名稱修飾static 減少符號汙染_In_reads_(len) SAL 註解volatile 或使用互斥體/自旋鎖