dietlibc 是一個極小的 C 標準庫(適合嵌入式/靜態鏈接),其 `strcpy` 實現同樣採用**字長級拷貝**,但代碼極度精簡,僅 30 行。本文通過還原宏為函數,逐步剖析其對齊檢查、字長拷貝主循環、尾部處理邏輯。
#include <stdint.h>
#include <string.h>
// 1. 構造魔數:0x01010101 / 0x80808080
static inline uint32_t MKW(uint32_t x) {
return x | (x << 8) | (x << 16) | (x << 24);
}
// 2. 計算指針對齊所需填充字節 (0~3)
static inline size_t STRALIGN(uintptr_t p) {
size_t r = p & 3;
return r ? 4 - r : 0;
}
// 3. 檢查源/目標指針是否同樣對齊
static inline int UNALIGNED(uintptr_t dst, uintptr_t src) {
return ((dst ^ src) & 3) != 0;
}
// 4. 取字長低字節
#define GFC(x) ((x) & 0xFF)
// 6. 字長右移 8 位 (移至下一字節)
#define INCSTR(x) do { (x) >>= 8; } while (0)
char* strcpy2(char* dst, const char* src) {
char* res = dst;
uint32_t l;
// A. 指針未對齊或互不對齊 → 退回逐字節
if (UNALIGNED((uintptr_t)dst, (uintptr_t)src)) {
while ((*dst++ = *src++));
return res;
}
// B. 目標指針未對齊 → 逐字節填充至對齊
size_t mis = STRALIGN((uintptr_t)dst);
while (mis-- && (*dst++ = *src++));
if (mis != (size_t)-1) return res; // 已遇 \0
// C. 主循環:字長級拷貝
while (1) {
l = *(const uint32_t*)src; // 載入 4 字節
// 檢測是否含 \0 (同 GLIBC 魔數法)
if ((((l - MKW(0x01)) & ~l) & MKW(0x80)) != 0) {
// 含 \0:逐字節完成剩餘
do { *dst++ = GFC(l); INCSTR(l); } while (GFC(l));
return res;
}
// 無 \0:整字長寫入,指針 +4
*(uint32_t*)dst = l;
src += 4;
dst += 4;
}
}
輸入: src="Hello World\0", dst=buffer
1. UNALIGNED 檢查: src%4==0 && dst%4==0? 是 → 進入字長路徑
2. STRALIGN(dst)=0 → 無需填充
3. 主循環:
l = "Hell" (0x6C6C6548)
零檢測: 否 → *(uint32_t*)dst = l; src+=4; dst+=4
l = "o Wo" → 同理
l = "rld\0" → 零檢測: 是
進入尾部處理:
*dst++ = 'r'; l>>=8
*dst++ = 'l'; l>>=8
*dst++ = 'd'; l>>=8
*dst++ = '\0'; l>>=8 → GFC(l)=0 結束
| 特性 | dietlibc strcpy | GLIBC strcpy |
|---|---|---|
| 代碼行數 | ~30 | ~100+ (含向量化路徑) |
| 架構專用優化 | 無 (純 C) | 有 (SSE2/AVX2/NEON 汇编) |
| 對齊處理 | 填充至對齊 | 類似 + 向量對齊 |
| 零檢測魔數 | 0x01/0x80 | 相同 |
| 目標場景 | 嵌入式/靜態鏈接/極小體積 | 通用高效能 |
作者在 VS 调试器中人為將 `src`/`dst` +1 導致未對齊,觀察到代碼正確退回逐字節路徑,驗證對齊檢查邏輯有效。