C语言——container_of宏介绍

简介

container_of 是 C 语言中常用的一个宏,通常用于从结构体的成员指针反向获取包含该成员的结构体指针。这个宏的作用非常重要,尤其是在处理嵌入式结构体或者链表时,经常会遇到这种情况:你得到一个结构体成员的指针,但你需要得到包含该成员的整个结构体的指针。

宏定义

#define container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - offsetof(type, member)))

参数说明:

  • ptr: 指向结构体成员的指针;
  • type: 包含该成员的结构体类型;
  • member: 结构体中成员的名称;

工作原理

  • offsetof(type, member) 计算的是成员在结构体中的偏移量;
  • (char *)(ptr) 将成员指针转换为 char *,然后减去偏移量,即得到结构体的起始地址;
  • 最终, (type *) 将这个地址转换为结构体类型的指针。

问:为什么要使用 (char *)(ptr)?

我们在使用 container_of 宏时,需要将结构体成员的指针转换成结构体的起始地址。要做到这一点,我们需要对结构体成员的偏移量进行运算。

在 C 语言中,结构体的成员是按照它们在结构体中声明的顺序连续排列的,所以结构体的成员有一个相对于结构体起始位置的偏移量。

举个例子:
假设有如下结构体:

struct example {
    int a;     // 偏移量 0
    char b;    // 偏移量 4
    double c;  // 偏移量 8
};

a 占用 4 字节,偏移量为 0;
b 占用 1 字节,偏移量为 4(因为 a 是 4 字节,所以 b 紧接着 a);
c 占用 8 字节,偏移量为 8。

假设我们现在有一个指向 b 成员的指针 ptr,如果我们想通过 ptr 获取到整个 example 结构体的指针,就需要知道 b 成员在结构体中的偏移量。

问:为什么要转换成 (char *)?

C 语言中指针的运算是基于指针类型的。也就是说,指针运算会以指针所指向类型的大小为单位进行。例如:

如果你有一个 int * 类型的指针,指针加 1 会增加 4 个字节(假设 int 是 4 字节);
如果你有一个 char * 类型的指针,指针加 1 会增加 1 个字节(因为 char 占 1 字节);
为了能够进行正确的内存地址计算,必须将 ptr 转换为 char * 类型,因为 char 是 C 中最小的存储单位,指针加 1 就表示内存地址加 1 字节。这使得我们可以通过指针运算精确地控制内存地址。

偏移量的计算:
我们知道 b 在结构体中的偏移量是 4 个字节,假设 ptr 是一个指向 b 的指针(类型为 char * 或 char * 的成员指针)。如果我们想通过 ptr 获取 example 结构体的起始地址,可以通过以下方式计算:

将 ptr 转换为 char * 类型,因为 char 的大小是 1 字节。
使用指针运算,减去偏移量,得到结构体的起始地址。

示例

假设有如下结构体:

struct person {
    char name[50];
    int age;
};

struct company {
    char name[100];
    struct person ceo;  // 嵌入一个 person 结构体
};

如果你有一个指向 person 结构体成员 ceo 的指针,并且想要获取 company 结构体的指针,可以使用 container_of 宏:

struct person *ceo_ptr = &company_instance.ceo;
struct company *company_ptr = container_of(ceo_ptr, struct company, ceo);

解析:

  • ceo_ptr 是指向 company 结构体中 ceo 成员的指针;
  • container_of 宏根据 ceo_ptr、结构体类型 struct company 和成员 ceo 的名字,反推回 company 结构体的指针;

应用场景

  • 内核编程:在 Linux 内核中,container_of 被广泛使用,尤其是在链表、队列和各种数据结构中。很多时候,内核函数会传递某个结构体的成员指针,而你需要根据该指针获取整个结构体指针;
  • 链表操作:链表节点通常是结构体的成员,使用 container_of 可以很方便地从链表节点获取整个结构体的指针;
posted @ 2025-01-17 21:00  岸南  阅读(29)  评论(0)    收藏  举报