[C/C++] 如何得知 structure 的 member 在經過 alignment 後的 pad size ( offsetof macro 和 container_of macro )
日前在 ptt 逛 C/C++ 程式板時,偶然看到有人詢問如何得知 structure 的 pad bytes 有多少,推文中有人提及一個例子。而推文者提及的方法,其中較令人玩味的地方,在於對 0 做 dereference 的動作,因為我之前沒有注意到這樣的用法,覺得這作法非常有趣,拜訪過 google 大神之後,大致上有些瞭解。
Jserv 在 C99 的 offsetof macro 中提及了這個方法是被定義在 C99 的 C 語言標準規範內,以下是傳統的實作方式,但是依據不同 compiler 應該會有不同的轉型,
#define offsetof(st, m)
((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))
Jserv 並引述了 Nigel Jones 在 Learn a new trick with the offsetof() macro 文中所介紹的實例,Jones 分別列舉了以下幾種在 Embedded System 開發中常會遇到的實際例子,解說 offsetof macro實務上的使用方式,
- pad bytes
- non-volatile memory
- protecting non-volatile memory
在我所看到的討論中,ptt 文中所討論的,就是需要 pad bytes 這個作法,藉由此作法,可以知道 structure 某個 member 的 padding offset 是多少,就可以正確取得此 member 的 memory address,藉此解決不同平台有不同的 memory alignment,所造成的 portability 的問題。
類似的用法也有出現在 Linux Kernel 中,其中較常被討論的是 container_of macro,被定義在 include/linux/kernel.h 中,
#define container_of(ptr, type, member) ({
const typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})
同樣基於 memory alignment 的考量,所以採用這個作法,可以協助程式瞭解該平台下,structure member 的正確 memory address。
在看到這個例子時,大部分開發人員的第一個反應,應該是訝異它對 0 做 dereference 的動作,但是因為此段程式碼是對 member 取址,所以並不會造成錯誤的記憶體存取,這樣的敘述不會造成錯誤。
Greg Kroah-Hartman 在 The Driver Model Core, Part I 文中提及 container_of 在 Linux Kernel 內實際應用,大家可以參考看看。
發佈留言
很抱歉,必須登入網站才能發佈留言。