(十)Linux内核中的常用宏container_of

Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。

Container_of的定义如下:

#define OffsetOf(type, member) ((unsigned long) &(((type *)0)->member))

#define container_of(p, type, member)  ((type *) ((char *)(p) - OffsetOf(type, member)))

1、其实它的语法很简单,只是一些指针的灵活应用,它分两步:

第一步,首先定义一个临时的数据类型(通过(type, member) ((unsigned long) &(((type *)0)->member))获得)member在结构体里的相对偏移地址。

第二步,用 ((type *) ((char *)(p)减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)。

其中的语法难点就是如何得出成员相对结构体的偏移量?

2、通过例子说明,如下:

#include <stdio.h>
#define OffsetOf(type, member)    ((unsigned long) &(((type *)0)->member))
typedef struct
{
    int num;
    char ch;
    float fl;
}test_struct;  

int main(void)
{
    printf("offsetof(test_struct, num) = %d\n",
            OffsetOf(test_struct, num));  

    printf("offsetof(test_struct,  ch) = %d\n",
            OffsetOf(test_struct, ch));  

    printf("offsetof(test_struct,  fl) = %d\n",
            OffsetOf(test_struct, fl));  

    return 0;
}

  例子输出结果:

  其中代码难以理解的地方就是它灵活地运用了0地址。如果觉得&(((type *)0)->member)这样的代码不好理解,那么我们可以假设在0地址分配了一个结构体变量test_struct a,然后定义结构体指针变量p并指向a(test_struct *p = &a),如此我们就可以通过&p->member获得成员member的地址。由于a的首地址为0x0,所以成员member的首地址为0x4。

  最后通过强制类型转换(unsigned long)把一个地址值转换为一个整数。

3、 分析完container_of的定义,接下来举个例子来体会一下它的使用方法。

例子,如下:

#include <stdio.h>
#define OffsetOf(type, member)    ((unsigned long) &(((type *)0)->member))
#define container_of(p, type, member)  ((type *) ((char *)(p) - OffsetOf(type, member)))

typedef struct
{
    int num;
    char ch;
    float fl;
}test_struct;  

int main(void)
{
    test_struct init_test_struct = { 99, ‘C‘, 59.12 };  

    char *char_ptr = &init_test_struct.ch;  

    test_struct *structs = container_of(char_ptr, test_struct, ch);  

    printf(" test_struct->num = %d\n test_struct->ch = %c\n test_struct->fl = %f\n",
        structs->num, structs->ch, structs->fl);  

    return 0;
}  

  输出结果:

  由此我们知道:#define container_of(p, type, member)  ((type *) ((char *)(p) - OffsetOf(type, member))) ,我们知道结构体type的地址为:结构体type的成员member的地址减去member的相对地址。这里(type *) ((char *)(p)指的是结构体type成员member的地址。

时间: 2024-10-13 20:53:56

(十)Linux内核中的常用宏container_of的相关文章

Linux内核中的常用宏container_of

/* linux-2.6.38.8/include/linux/compiler-gcc4.h */ #define __compiler_offsetof(a,b) __builtin_offsetof(a,b) /* linux-2.6.38.8/include/linux/stddef.h */ #undef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,

(六)linux内核中的offsetof与container_of宏

参考: offsetof与container_of宏[总结] #define offsetof(type, member) (size_t)&(((type*)0)->member) #define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );}) 原文地

linux内核中的宏ffs(x)【转】

转自:https://www.cnblogs.com/fengeryi/p/3449720.html linux内核中ffs(x)宏是平台相关的宏,在arm平台,该宏定义在 arch/arm/include/asm/bitops.h #define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); }) static inline int fls(int x) { int ret; if (__builtin_constant_p(x)) r

Linux内核中双向链表的经典实现

Linux内核中双向链表的经典实现 概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核中非常常用的两个经典宏定义offsetof和container_of.内容包括:1. Linux中的两个经典宏定义2. Linux中双向链表的经典实现 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3562146.html 更多

Linux 内核中的 GCC 特性

转载:http://www.ibm.com/developerworks/cn/linux/l-gcc-hacks/?S_TACT=105AGX52&S_CMP=tec-csdn Linux 内核中的 GCC 特性 了解用于 C 语言的 GCC 扩展 Linux? 内核使用 GNU Compiler Collection (GCC) 套件的几个特殊功能.这些功能包括提供快捷方式和简化以及向编译器提供优化提示等等.了解这些特殊的 GCC 特性,学习如何在 Linux 内核中使用它们. 0 评论:

Linux内核中常用String库函数实现

//只列举了部分常用的strcpy,strcmp,strcat,strchr,strstr,strpbrk...  char *strcpy(char *dest, const char *src) { char *tmp = dest; while ((*dest++ = *src++) != '\0') /* nothing */; return tmp; } char *strncpy(char *dest, const char *src, size_t count) { char *t

Linux内核中的jiffies及其作用介绍及jiffies等相关函数详解

在LINUX的时钟中断中涉及至二个全局变量一个是xtime,它是timeval数据结构变量,另一个则是jiffies,首先看timeval结构struct timeval{time_t tv_sec; /***second***/susecond_t tv_usec;/***microsecond***/}到底microsecond是毫秒还是微秒?? 1秒=1000毫秒(3个零),1秒=1000 000微秒(6个零),1秒=1000 000 000纳秒(9个零),1秒=1000 000 000

向linux内核中添加外部中断驱动模块

本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内核中添加外部中断驱动模块.7.完整驱动程序代码.linux的内核版本为linux2.6.32.2. 一.linux模块的框架以及混杂设备相关知识 1.内核模块的框架如下图所示,其中module_init()(图中有误,不是modules_init)只有在使用insmod命令手动加载模块时才会被调用,

linux 内核 中链表list

这个结构从list.h 移到了types.h, 可见内核对循环链表的重视 include/linux/types.h中定义 struct list_head {        struct list_head *next, *prev;}; include/linux/list.h 中的宏 初始化 一个叫name的链表节点 #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \        s