# quiz6 測驗6 問題 詳解 ###### tags: `linux2022` `#define ALIGNOF(t)` 拆步驟解析時 1. `(struct { char c; t _h; } *)0)`:將 `struct { char c; t _h; }` 設成指標型態 `*`,並將**指標位址設為0** 2. `((struct { char c; t _h; } *)0)->_h`:**取得**該 struct pointer中的 **`_h` 成員** 3. `&((struct { char c; t _h; } *)0)->_h`:加`&` 取得 **`_h` 成員記憶體位址** 4. `(char *)(&((struct { char c; t _h; } *)0)->_h)`:將 **`_h` 成員記憶體位址** 強制轉型成 `(char *)` 型態 參照 [The (char *) casting in container_of() macro in linux kernel](https://stackoverflow.com/questions/20421910/the-char-casting-in-container-of-macro-in-linux-kernel) 5. `((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0)` :和`(char *)0` 位址相減後,即為 `t _h` 的 alignment 我卡在 步驟4 將 **`_h` 成員記憶體位址** 強制轉型成 `(char *)` 型態,**為何就能轉換出 ALIGNOF 差距的記憶體格數?** 有參照 [The (char *) casting in container_of() macro in linux kernel](https://stackoverflow.com/questions/20421910/the-char-casting-in-container-of-macro-in-linux-kernel) 文章,但是還是看不懂 ### 解法 一對一討論時,Jserv老師有提到 C語言規格 所有的C語言指標型態 **都能轉型成 pointer to character `(char *)`** 但是有伴隨著alignment的infaulsment 在強制轉型時,遇到alignment時,會有額外的alignment struct即為連續的記憶體,所以一定能對struct做轉型 但是轉型前一定要確保是 **指標型態** 因為所有的指標型態都可轉行成 pointer to character `(char *)` 在[C99規格書](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf)中 用 alignment 關鍵字搜尋 ![](https://hackmd.io/_uploads/B16X-eT72.png) 和 pointer 的章節有 ``` alignment, 3.2 pointer, 6.2.5, 6.3.2.3 structure/union member, 6.7.2.1 ``` ## 6.3.2.3 Pointers 7. A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. **If the resulting pointer is not correctly $aligned^{(57}$** for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. **When <span class="blue">a pointer**</span> to an object **is <span class="blue">converted to</span> a pointer to a <span class="blue">character type**</span> , **the result points to the <span class="blue">lowest addressed byte</span> of the object.** Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object. **$aligned^{(57}$** 的註釋如下 57)In general, the concept ‘‘correctly aligned’’ is transitive: **if a pointer to type A is correctly aligned for a pointer to type B**, which in turn is correctly **aligned for a pointer to type C, then a pointer to type A is correctly aligned for a pointer to type C.** `(char *)(&((struct { char c; t _h; } *)0)->_h) ` 或 `(char *)(&((struct { char c; char _h; } *)0)->_h) ` 用GDB無法直接印出來,得宣告成變數才能印出 因此將 `struct { char c; t _h; }` 宣告成結構變數 --- ```c= #include <stdio.h> /* #define ALIGNOF(t) \ ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0) */ struct ALIGNOF1{ char c; char _h; }; //char _h 1 byte struct ALIGNOF2{ char c; short _h; };//short _h 2 byte struct ALIGNOF3{ char c; int _h; };//int _h 8 byte int main() { struct ALIGNOF1 p1; struct ALIGNOF2 p2; struct ALIGNOF3 p3; return 0; } ``` GDB trace ``` gcc -g main.c -o test gdb ./test (gdb) b 15 (gdb) r (gdb) p sizeof(p1) $1 = 2 (gdb) p sizeof(p2) $2 = 4 (gdb) p sizeof(p3) $3 = 8 (gdb) p &p1 $4 = (struct ALIGNOF *) 0x7fffffffdd68 (gdb) p &p1.c $5 = 0x7fffffffdd68 "" (gdb) p &p1._h $6 = 0x7fffffffdd69 "" (gdb) p &p2 $7 = (struct ALIGNOF2 *) 0x7fffffffdd68 (gdb) p &p2.c $8 = 0x7fffffffdd68 "" (gdb) p &p2._h $9 = (short *) 0x7fffffffdd6a (gdb) p &p3 $10 = (struct ALIGNOF3 *) 0x7fffffffdd68 (gdb) p &p3.c $11 = 0x7fffffffdd68 "" (gdb) p &p3._h $12 = (int *) 0x7fffffffdd6c ``` 可繪出以下記憶體配置 ![](https://hackmd.io/_uploads/Hk94-e6mn.png) 此為 [Data Alignment](https://hackmd.io/@sysprog/c-memory?type=view#data-alignment) 特性 ### 進階分析 只用 & 取址相減,GDB無法判斷 ``` (gdb) p &p1._h-&p1 First argument of `-' is a pointer and second argument is neither an integer nor a pointer of the same type. (gdb) p (&p1._h-&p1) First argument of `-' is a pointer and second argument is neither an integer nor a pointer of the same type. (gdb) p (&p1._h) - (&p1) First argument of `-' is a pointer and second argument is neither an integer nor a pointer of the same type. ``` :::warning 原因出在 ``` and second argument is neither an integer nor a pointer of the same type. ``` 第二個參數(p1) 和第一個參數(p1._h) **不是相同的指標型態** ::: 因此 & 取址後,得強制轉型成 `(char *)` 因為兩個參數,都強制轉型統一成`(char *)`指標型態,才能相減 ``` (gdb) p (char *) (&p1._h) $13 = 0x7fffffffdd69 "" (gdb) p (char *) (&p1) $14 = 0x7fffffffdd68 "" (gdb) p (char *) (&p1._h) - (char *)(&p1) $15 = 1 (gdb) p (char *) (&p2._h) - (char *)(&p2) $16 = 2 (gdb) p (char *) (&p3._h) - (char *)(&p3) $17 = 4 ``` ```c= #include <stdio.h> #define ALIGNOF(t) \ ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0) struct ALIGNOF1{ char c; char _h; }; //char _h 1 byte struct ALIGNOF2{ char c; short _h; };//short _h 2 byte struct ALIGNOF3{ char c; int _h; };//int _h 8 byte int main() { struct ALIGNOF1 p1; struct ALIGNOF2 p2; struct ALIGNOF3 p3; printf("%ld\r\n",(char *) (&p1._h) - (char *)(&p1)); printf("%ld\r\n",(char *) (&p2._h) - (char *)(&p2)); printf("%ld\r\n",(char *) (&p3._h) - (char *)(&p3)); printf("ALIGNOF(char)=%ld\r\n",ALIGNOF(char)); printf("ALIGNOF(short)=%ld\r\n",ALIGNOF(short)); printf("ALIGNOF(int)=%ld\r\n",ALIGNOF(int)); return 0; } ``` ``` $ gcc -g main.c -o test $ ./test 1 2 4 ALIGNOF(char)=1 ALIGNOF(short)=2 ALIGNOF(int)=4 ```