# 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 關鍵字搜尋

和 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
```
可繪出以下記憶體配置

此為 [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
```