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

字串操作效能陷阱:從 CString 與 strcat 看「黑箱」背後的真相

     編輯:VC 教學

前言:一個看似正確卻極慢的需求

需求:將檔案二進位內容以十六進位文字格式輸出(如 4D 5A 90 00 ...)。直覺寫法:讀檔 → 迴圈 wsprintf 格式化每位元組 → lstrcat 串接 → 寫檔。程式碼無語法錯誤、結果正確,但速度慢得驚人,進度條越跑越慢。

元兇現形:strcat 的 O(N²) 災難

查看 CRT 原始碼:

char* __cdecl strcat(char* dst, const char* src) {
    char* cp = dst;
    while (*cp) cp++;           // 尋找結尾 \0
    while (*cp++ = *src++);     // 複製 src
    return dst;
}

每次 lstrcat 都必須從頭掃描尋找結尾。迴圈跑 10 萬次,前 1 萬字元被掃描 10 萬次 → 總操作量呈平方級增長。這就是進度條越跑越慢的真相。

優化方案一:手動維護尾端指標(以空間換時間)

void Save() {
    // ... 讀檔、配置緩衝區 lpDst ...
    DWORD j = 0;  // 指向 lpDst 目前尾端
    for (DWORD i = 0; i < dwSize - 1; i++) {
        if (i % 16 == 15) {  // 換行
            wsprintf(szByte, "%02X ", lpbySrc[i]);
            k = 4;  // "XX " 長度
        } else {
            wsprintf(szByte, "%02X ", lpbySrc[i]);
            k = 3;  // "XX " 長度
        }
        lstrcpy(&lpDst[j], szByte);  // 直接複製到尾端
        j += k;
    }
    // 處理最後一位元組
    wsprintf(szByte, "%02X", lpbySrc[i]);
    lstrcpy(&lpDst[j], szByte);
    // ... 寫檔、釋放 ...
}

避免重複掃描,速度提升數百倍。

深入黑箱:CString 為什麼也不夠快?

MFC CString::ConcatCopy 使用 memcpy 而非 strcat,避免尋找結尾:

void CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
                         int nSrc2Len, LPCTSTR lpszSrc2Data) {
    int nNewLen = nSrc1Len + nSrc2Len;
    if (nNewLen != 0) {
        AllocBuffer(nNewLen);
        memcpy(m_pchData, lpszSrc1Data, nSrc1Len * sizeof(TCHAR));
        memcpy(m_pchData + nSrc1Len, lpszSrc2Data, nSrc2Len * sizeof(TCHAR));
    }
}

但 CString::operator+= 內部呼叫 ConcatCopy,每次串接會重新配置緩衝區並複製兩次(舊資料 + 新資料),頻繁迴圈下效能同樣慘不忍睹。

黑箱啟示錄

  • C 風格字串:「零結尾」機制導致 strcat 必須掃描尾端,適合偶爾串接,不適合高頻迴圈。
  • CString:內部記錄長度避免掃描,但每次 += 觸發重新配置與雙重複製,同樣不適合大量迴圈串接。
  • 最佳實務:預估總長度 → 一次配置足夠緩衝區 → 維護寫入位移指標 → 直接 memcpy/lstrcpy 填充 → 最後一次性寫入。

結語

標準庫與框架類別是「黑箱」,平時假設完美直接用沒問題。但效能敏感場景下,必須理解黑箱內部機制,才能避開隱藏陷阱。下次寫大量字串拼接時,請記得:預先配置、手動維護指標、避免重複掃描與複製。

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