# 【Linux】Linux核心原始程式碼巨集 container_of 以及 offsetof
## 簡介
這兩個巨集對於Linux核心原始程式碼非常的重要,它適用的範圍非常廣泛,被應用在**Linked List**與**hash Table**這一通用的資料結構中,用來化簡程式設計,並讓C語言也具備物件導向的行為,也是Linux物件導向中相當重要的機制!
## [offsetof 巨集](https://github.com/torvalds/linux/blob/master/tools/include/linux/kernel.h)
```c
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
```
- **目的** : 取得結構體內成員相對於結構體起始位置的位移量
- `size_t` : 表示一個byte的單位
:::success
**操作細節**
* `(TYPE *)0` : 將 `0` (數值意義上)轉型成 `type` 型別的指標
* `&((TYPE *)0)->MEMBER` : 取得其成員記憶體位址
:::
> 編譯器處理 `&((type *)0)->member` 時,不會真正去存取地址為 0 的記憶體區段,只是將 0(數值意義上) 看做指標操作的一個運算元,除非額外有**dereference**操作。 https://hackmd.io/@sysprog/linux-macro-containerof
## [container_of 巨集](https://github.com/torvalds/linux/blob/master/include/linux/container_of.h)
```c
#ifndef container_of
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
```
- **目的** : 給定結構體內某成員的指標,求得原結構體的記憶體位置
- **應用** :
- **模擬繼承與多態** : 常用於模擬 C 語言中的「繼承」,使子結構體可以引用到父結構體的屬性或方法。
- **通用資料結構設計** : **雙向列表(doubly Linked List)** 和 **哈希表(Hash-Table)** 在Linux核心原始程式碼中採用這樣設計模式,使其能夠嵌入至任意資料結構中並進行操作
**操作細節**
* 使用`__typeof__` 取得 `type` 中 `member` 的資料型態,並且將 `ptr` 指派給 `__pmember`
* `__pmember` 代表該 `type` 資料結構中成員的資料型態
* 接著透過 `offsetof(type, member)` 取得 `member` 在 `type` 中的offset
* 將絕對地址 `(char *) __pmember` 減去 `offsetof(type, member)` 即可得到結構體的起始位
> 轉型成 `char *` 目的是確保地址操作是以 **1 byte** 為基準,由於 `char *` 不論在哪裡都是1 byte(C語言標準),藉由這樣轉換方式能夠確保指標操作不會有問題
下面這張清楚地說明 `container_of` 的操作

可以透過`contain_of`設計以下的物件導向的概念
- **封裝(encapsulation)**
- **繼承(inheritance)**
- **多型(polymorphism)**
:::success
在C語言上可以透過函數指標將方法綁定在特定資料結構上,所以C語言是需要透過一些語法或巨集來達成封裝的效果,雖然有語法的限制,但並不影響物件導向的精神
:::
> 物件導向是一種態度,語法只是輔助
> Jserv
## Example
```cpp
#include <linux/kernel.h>
#include <stddef.h>
#include <stdio.h>
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
// struct data
// {
// short a;
// char b;
// double c;
// }__attribute__((packed)); // align memory
struct data
{
short a;
char b;
double c;
};
int main(int argc, char **argv)
{
struct data x = {
.a = 25,
.b = 'A',
.c = 12.45
};
char *p = (char *) &x;
printf("a=%d\n", *((short *) p));
p += sizeof(short);
printf("b=%c\n", *((char *) p));
p += sizeof(char);
printf("c=%lf\n", *((double *) p));
printf("\n*****use offsetof *****\n");
p = (char *) &x;
printf("a=%d\n", *((short *) p));
p += offsetof(struct data, b);
printf("b=%c\n", *((char *) p));
p = (char *) &x;
p += offsetof(struct data, c);
printf("c=%lf\n", *((double *) p));
return 0;
}
```
## 參考資料
1. [Linux 核心原始程式碼巨集: container_of](/odsx15lMRDiqsuQDL8LE8g)