前言
昨日在寫 Python Socket Programming 作業時,遇到了一個關於 struct 模組的有趣的現象,記錄一下。
正文
情境是這樣的,我想要將一個正整數以及一個長度 3 的字串跟一個正整數結構化成 byte 的形式
很直覺的使用了
1 | import struct |
但這個輸出並不符合我的預期,正整數 4 bytes
+ 長度三的字串 3 bytes
+ 正整數 4 bytes
,結果應該要是 11 bytes
。但是輸出卻是 \x01\x00\x00\x00abc\x00\x01\x00\x00\x00
-> 12 bytes
於是使用 calcsize 確認一下
1 | struct.calcsize("I3sI") |
的確也是 12 bytes
,而且可以發現的是,是中間的字串變成了 4 bytes
於是實驗了一下:
1 | struct.calcsize("3s") |
可以發現 3s
的確是 3 bytes
,但是當後面還有有其他 bytes 的話,則會補齊 4 bytes
,然而我用 4sI
可以發現還是 8 bytes
無誤。
第一個直覺就是猜想,是不是 struct 自動做了 word 大小為 4 的對齊。
查了一下文件
By default, C types are represented in the machine’s native format and byte order, and properly aligned by skipping pad bytes if necessary (according to the rules used by the C compiler).
到這邊真相已經大白,但是還是沒解決問題
這個問題的解法就是在 fmt 前面補上一些符號來告訴 struct 模組需不需要自動做對齊
1 | struct.pack("3sI", b'abc', 1) |
文件還有貼心的寫下
The form ‘!’ is available for those poor souls who claim they can’t remember whether network byte order is big-endian or little-endian.
1 | struct.pack("!3sI", b'abc', 1) |
這些符號總共有 @
, =
, <
, >
, !
,有興趣可以到官方文件了解。有趣的是只有第一個(@
),也就是預設的模式會採用自動對齊!