當函數返回指標 T* 時,調用方獲得對底層數據的直接訪問權。若實現方意圖提供只讀視圖 (如配置表、錯誤字符串、單例快照),必須在類型系統層面強制約束,而非依賴文檔或約定。
const T*
/* 模塊內部:只讀數據源 */
static const int g_ConfigTable[] = { 100, 200, 300, 400 };
/* 對外接口:承諾不修改 */
const int* GetConfigValue(int index)
{
if (index >= 0 && index < 4)
return &g_ConfigTable[index];
return NULL;
}
/* 調用方 */
void UseConfig()
{
const int* p = GetConfigValue(2); // 編譯器強制 const
printf("Value: %d\n", *p); // ✅ 讀取合法
// *p = 999; // ❌ 編譯錯誤: assignment of read-only location
}
MSVC (cl.exe): error C3892: 'p' : you cannot assign to a variable that is const
GCC/Clang: error: assignment of read-only location '*p'
這是編譯期攔截,零運行時開銷。
const char* GetErrorString(int errCode)
{
static const char* const msgs[] = {
[0] = "Success",
[1] = "Invalid Parameter",
[2] = "Out of Memory",
[3] = "Device Not Found"
};
return (errCode >= 0 && errCode < 4) ? msgs[errCode] : "Unknown Error";
}
// 返回類型 const char* 確保調用方不可修改字面量
typedef struct { int id; const char* name; double ratio; } SensorCalib;
const SensorCalib* GetCalibration(int sensorId)
{
static const SensorCalib table[] = {
{1, "Temp", 0.01},
{2, "Pressure", 0.001}
};
// 返回指向 const 結構體的指標,雙重保護:
// 1. 結構體成員不可修改
// 2. name 指向的字符串也不可修改 (const char*)
return (sensorId >= 1 && sensorId <= 2) ? &table[sensorId-1] : NULL;
}
/* 讀取時複製一份不可變快照 (無鎖讀) */
const SensorCalib* GetCalibrationSnapshot(int sensorId, SensorCalib* outBuf)
{
static const SensorCalib table[] = { ... };
if (sensorId < 1 || sensorId > 2) return NULL;
*outBuf = table[sensorId-1]; // 複製到調用方提供的緩衝區
return outBuf; // 返回指向調用方緩衝區的 const 指標
}
// 調用方擁有 outBuf 存儲,無共享狀態,天然線程安全
| 誤區代碼 | 問題 | 修正 |
|---|---|---|
int* Get() { static int x; return &x; } | 返回非 const,調用方可修改全局狀態 | const int* Get() + 內部 static const int |
char* GetStr() { return "literal"; } | 字面量類型是 const char[],隱式轉換丟棄 const (C++非法,C警告) | const char* GetStr() |
const int* p = Get(); int* q = (int*)p; *q = 1; | 強制轉型繞過保護,導致未定義行為 | 嚴禁 const_cast/C式轉型,除非確知底層可寫 |
| 返回指向棧變量地址 | 懸空指標,與 const 無關 | 參考《返回指標函數四大陷阱》一文 |
const T*。static const T g_Data[] = ...,編譯器+鏈接器+MMU 三重保護。@return 指向只讀數據的指標,生命週期為進程全周期,調用方不得修改。-Wwrite-strings -Wcast-qual -Wmissing-prototypes,CI 強制通過。std::string_view/span/std::optional 替代裸指標。| 返回類型 | 語義 | 典型用例 | 調用方責任 |
|---|---|---|---|
T* | 可修改,調用方擁有所有權 (需 free) 或指向可變緩衝區 | malloc, strdup, 輸出緩衝區 | 必須釋放或不修改 (視契約) |
const T* | 只讀視圖,數據由被調用方管理,生命週期通常為靜態/進程級 | 查找表、錯誤字符串、配置、單例 | 僅讀取,不釋放,不轉型 |
T* const | 指標本身不可變 (C語言無此返回類型語義,屬變量聲明) | — | — |
T* + __attribute__((malloc)) | 標註為分配函數,靜態分析器追蹤釋放 | 自定義分配器 | 必須成對 free |
const T* 作為返回類型,是 C 語言零成本、編譯期強制、類型安全的只讀保護機制。它將「不可修改」從文檔約定提升為類型系統契約,讓編譯器成為第一道防線。配合內部 static const 數據源,構成「只讀數據發布」的完整閉環。這是編寫健壯、可維護、可靜態分析的 C 庫接口的必備技能。