函數返回指向內部 static 緩衝區的指針,看似避免了堆分配與用戶傳參麻煩,實則暗藏嚴重陷阱:非線程安全、多次調用覆蓋、表達式求值順序導致邏輯錯誤。本文以經典 inet_ntoa 為例深度剖析。
| 策略 | 優點 | 缺點 |
|---|---|---|
| 堆分配 (malloc/new) | 生命週期可控 | 易洩漏、雙重釋放、調用者需釋放 |
| 調用者傳緩衝區 | 所有權清晰、無洩漏 | API 麻煩、需傳長度、緩衝區溢出風險 |
| 內部 static 緩衝區 | API 簡潔、無需釋放 | ⚠️ 共享緩衝區、非線程安全、表達式求值覆蓋 |
char* inet_ntoa(struct in_addr in) 內部使用 static char buf[16]。
struct in_addr src, dst;
src.s_addr = 3776385196; // 172.16.23.225
dst.s_addr = 1678184620; // 172.16.7.100
fprintf(fp, "源IP<%s>\t目的IP<%s>\n", inet_ntoa(src), inet_ntoa(dst));
結果:文件中兩個 IP 完全相同(均為 src)。
原因:參數求值順序未定義(通常右到左)。先算 inet_ntoa(dst) 返回 buf 地址,再算 inet_ntoa(src) 覆蓋 buf 內容,最終 fprintf 兩個參數指向同一緩衝區(已被 src 覆蓋)。
man page 警告:The string is returned in a statically allocated buffer, which subsequent calls will overwrite.
if (strcmp(inet_ntoa(ip1), inet_ntoa(ip2)) == 0) { ... }
// 永遠為真!因為兩次調用返回同一地址,strcmp 比較的是同一字符串
std::string ip = inet_ntoa(addr); 或 strcpy(buf, inet_ntoa(addr));inet_ntop(需調用者提供緩衝區)、C++ std::string 返回值優化(RVO/移動語義)「不要試圖返回對象的引用/指針指向內部 static 變量」——同樣理由:共享狀態導致不可預期行為。
API 設計要權衡便利性與正確性。static 緩衝區返回模式在併發、複雜表達式、長生命週期場景下極其危險。現代 C++ 應優先選用值語義(std::string、std::vector)或輸出參數,讓編譯器與所有權機制保護你。