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

關於記憶體對齊的深度解析與實戰經驗

     編輯:關於VC++

前言:一個令人抓狂的 Bug

曾接手一個網路視訊監控程式,上層軟體透過控制指令取得視訊板資料。開發過程順利,測試時卻發現取得的視訊資料流錯誤。單步偵錯至發送視訊指令,回傳「指令操作錯誤」。資料封包內容顯示無誤,卻怎麼也跑不通。花了一整晚,偶然將 sizeof() 計算的結構體大小改為手動 BYTE 陣列拷貝,程式竟然正常執行!原來發送給網路板的資料封包大小根本不是實際控制資料封包的大小。

根本原因:編譯器的結構體對齊優化

想起 Stanley B. Lippman 《C++ 物件模型》提過:編譯器為提升 CPU 效率,會對 struct 結構進行優化,利用 sizeof 可得不同平台優化後的大小。以前看過卻未深究,如今吃盡苦頭才明白。

C++ 編譯器為讓 CPU 效能達最佳,會對 struct 記憶體結構進行資料對齊 (32 位機為 4 的倍數,64 位機為 8 的倍數)。例如:

#include <iostream>
typedef struct tagPACKAGE {
    int intValue;
    char chValue1;
    char chValue2;
} PACKAGE, *LP_PACKAGE;

int main() {
    PACKAGE package;
    std::cout << "sizeof(package) = " << sizeof(package) << std::endl;
    return 0;
}

執行結果

輸出為 8 (sizeof(int) + sizeof(char) + sizeof(char) + 對齊填充 = 4+1+1+2=8)。編譯器在背後塞入無意義的填充位元組以達成對齊。

為何先前網路程式沒問題?

推測因雙端皆為 Windows XP,對齊規則一致;而當前網路視訊板為嵌入式 Linux,對純資料 (未對齊) 敏感,導致封包大小不符而失敗。

解決方案:#pragma pack 強制指定對齊

#pragma pack(push)
#pragma pack(1)               // 1-byte alignment
typedef struct tagPACKAGE {
    int intValue;
    char chValue1;
    char chValue2;
} PACKAGE, *LP_PACKAGE;
#pragma pack(pop)             // 恢復預設對齊

typedef struct tagPACKAGE2 {  // 無 pack 控制,採預設對齊
    int intValue;
    char chValue1;
    char chValue2;
} PACKAGE2, *LP_PACKAGE2;

int main() {
    PACKAGE package;
    PACKAGE2 package2;
    std::cout << "sizeof(package) = " << sizeof(package) << std::endl;   // 7
    std::cout << "sizeof(package2) = " << sizeof(package2) << std::endl; // 8
    return 0;
}

對齊控制結果

對指定封包使用 #pragma pack(1) 可防止編譯器插入填充位元組,確保封包大小與協定定義一致。

總結

C++ 編譯器在背後做了許多優化。處理網路資料或與嵌入式裝置溝通時,務必小心記憶體對齊問題,善用 #pragma pack 控制結構體佈局。

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