C语言宏函数container of()怎么使用

发布时间:2021-12-20 09:08:26 作者:iii
来源:亿速云 阅读:160
# C语言宏函数container_of()怎么使用

## 引言

在Linux内核开发中,`container_of()`宏是一个极其重要且常用的工具。它能够通过结构体成员的地址反向推导出整个结构体的起始地址,这种技术在链表、设备驱动等场景中广泛应用。本文将深入解析`container_of()`的原理、使用方法以及实际应用案例。

---

## 一、container_of()宏概述

### 1.1 什么是container_of()

`container_of()`是Linux内核中定义的一个宏,其作用是通过结构体中某个成员的地址,计算出该结构体的起始地址。这种技术在内核数据结构(如链表、设备驱动模型)中被频繁使用。

### 1.2 宏定义源码

```c
// Linux内核中的定义(简化版)
#define container_of(ptr, type, member) ({              \
    const typeof(((type *)0)->member) *__mptr = (ptr); \
    (type *)((char *)__mptr - offsetof(type, member)); })

二、container_of()工作原理

2.1 关键机制:offsetof

offsetof(type, member)是一个标准库宏,用于计算结构体成员相对于结构体起始地址的偏移量。例如:

struct sample {
    int a;
    char b;
    double c;
};

// 假设double在64位系统偏移量为8
size_t offset = offsetof(struct sample, c); // 可能返回8

2.2 计算步骤解析

container_of(&obj->member, struct obj, member)为例: 1. 通过typeof获取成员的类型并做类型检查 2. 使用offsetof计算成员偏移量 3. 将成员指针转换为char*(确保字节级计算) 4. 减去偏移量得到结构体起始地址


三、使用场景与示例

3.1 Linux链表中的应用

内核链表list_head的典型用法:

struct task_struct {
    int pid;
    struct list_head tasks; // 嵌入的链表节点
};

// 通过tasks节点获取task_struct
struct task_struct *task = container_of(ptr, struct task_struct, tasks);

3.2 实际代码示例

#include <stdio.h>
#include <stddef.h>

#define container_of(ptr, type, member) ({              \
    const typeof(((type *)0)->member) *__mptr = (ptr);  \
    (type *)((char *)__mptr - offsetof(type, member)); })

struct student {
    int id;
    char name[20];
    float score;
};

int main() {
    struct student stu = {101, "Alice", 95.5};
    float *score_ptr = &stu.score;
    
    // 通过score成员反推student结构体
    struct student *s = container_of(score_ptr, struct student, score);
    
    printf("ID: %d, Name: %s\n", s->id, s->name);
    return 0;
}

输出结果:

ID: 101, Name: Alice

四、常见问题与注意事项

4.1 类型安全检查

typeof和指针强制转换确保了类型安全。如果错误地传递了成员名,编译时会报错。

4.2 非GCC编译器的兼容性

原始实现依赖GCC扩展语法({...}),在其他编译器中可能需要改写:

// 替代方案
#define container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - offsetof(type, member)))

4.3 性能影响

container_of()在运行时只有简单的指针运算,没有性能开销。


五、高级应用技巧

5.1 嵌套结构体处理

对于多层嵌套的结构体,可以链式调用:

struct A {
    struct B b;
};

struct B {
    int val;
};

struct B *b_ptr = &obj.a.b;
struct A *a_ptr = container_of(b_ptr, struct A, b);

5.2 结合typeof动态获取类型

#define get_container(ptr, member) \
    container_of(ptr, typeof(*ptr), member)

六、与其他技术的对比

6.1 与C++的offsetof对比

C++中可以通过:

reinterpret_cast<T*>(reinterpret_cast<char*>(ptr) - offsetof(T, member))

实现类似功能,但缺乏编译时类型检查。

6.2 与面向对象设计的关联

这种技术实现了类似”子类通过基类指针向上转型”的效果。


七、内核实际案例

7.1 进程调度器中的使用

kernel/sched/core.c中,通过task_structse成员获取任务指针:

struct task_struct *p = container_of(se, struct task_struct, se);

7.2 设备驱动模型

struct device嵌入到具体设备结构中时,常用此宏反向引用。


八、总结

container_of()宏展示了C语言指针操作的强大能力,其核心思想可以归纳为: 1. 通过成员地址减去偏移量得到基地址 2. 依赖编译器的类型检查保证安全 3. 在资源受限环境下实现高效的对象定位

掌握这个宏对于理解Linux内核和开发高性能C程序至关重要。


附录:完整参考实现

#ifndef container_of
#define container_of(ptr, type, member) ({              \
    const typeof(((type *)0)->member) *__mptr = (ptr);  \
    (type *)((char *)__mptr - offsetof(type, member)); })
#endif

注意:实际内核代码还包含额外的编译器属性检查,本文为简化说明进行了适当裁剪。 “`

推荐阅读:
  1. 一、3:创建django项目及其补充
  2. 一、2 基于wsgiref定义自己的web框架

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

c语言

上一篇:centos7下如何搭建DNS服务器

下一篇:Linux下Apache服务如何部署和配置

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》