alignment 其實不僅僅發生在structure, 只是structure 內的alignment 比較會引起注意!
如同dayi 所說, 這跟CPU/OS/Compiler 息息有關
笨阿呆... 小神童C 語言比較當中, 應該就有這一段, 你白看嚕!
alignment 最主要來自CPU(硬體)的先天限制, 大家都習慣Intel 的x86 CPU, 所以對alignment 就相對觀念模糊, 如果從硬體的角度去觀看這件事情, 或許相對簡單很多.
當CPU 是32 bits, 代表CPU 每次存取記憶體, 就是一次處理32 bits, 站在讓CPU 發揮最大效能的角度上, 每次記憶體的讀取/改變/寫入, 當然是使用32bits 處理, 當你企圖變更非32 bits 的資料, 例如你改變byte 1/2, CPU 必須很費事的, 先讀取32 bits 資料, 變更bit [8..23] & 保留bit[0..7, 24..31], 然後把32 bits 資料寫回去!!
Intel CPU 因為從8088/8086.. 一路到現在, 為了跟以前相容, 所以始終容許你在非CPU bus 容量的邊界條件上恣意存取記憶體, 但是, 相對讓CPU 效能大打折扣, 如上面所說的, 你的一個簡單寫入動作, 硬體其實是做了:讀取, 遮罩變更, 寫入三個動作!!! 很多沒有歷史包袱的CPU 是不容許這樣玩耍的!! 那.. 如果CPU 不容許.. OS/Compiler 能搞啥? 呵, 當然, CPU 不容許, 意味著, 當程式這樣蠻幹, 硬體例外事件(Exception)就會被觸發, 所以, OS 有機會改變這項不容許變成容許, 只是, 成本就更高了, 當你存取bit [8..23], 硬體產生例外處理, 然後OS 接手, 用一長串的程式碼去完成你要的16 bits 存取 延續上面的例子, 更複雜的現象就是你讀取/存入byte3/4, 當讀取時, CPU bus 要讀取兩次資料, 然後組合成你要的16 bits 資料給你, 當你寫入, 就需要讀取-變更-寫入進行兩個cycle
x86 上的compiler 為了解決上述問題, 所以, 就有alignment 設定出現, 也是為了跟過往相容, 當你的程式沒有任何包袱存在, 要跑32 bits platform, 最好就是32 bits alignment, 但是如果有包袱, 例如上面阿呆列的那個Windows BMP header, 就得乖乖設定成16 bits alignment !!
再換另一個角度去思考這個問題, 如果CPU 不支援非alignment 上的存取, OS 又不support 呢?
那就是乖乖寫程式處理, 所以, 當你看網路上很多open source 的程式碼, 有時會看到, 很多資料從硬碟內讀取進來, 都不是直接使用對應structure, 而是讀到一個記憶體區塊, 然後才逐步把資料一項一項填寫入structure 內, 不要以為寫程式的人發神經, 其實那才是一個最正確的程式寫法, 這可以確保, 程式不管移植到任何CPU/OS platform, 他都保證正確執行的方法!!! 通常這類程式, 你能看到的不僅僅是alignment 問題, 還包括endian 問題
Acute.
|