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

container_of的定义如下:

#define container_of(ptr, type, member) ({			\
	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})
其实它的语法很简单,只是一些指针的灵活应用,它分两步:
第一步,首先定义一个临时的数据类型(通过typeof(((type *)0)->member)获得)与ptr相同的指针变量__mptr,然后用它来保存ptr的值。
第二步,用(char *)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)。

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

通过例子说明,如清单1:

#include <stdio.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

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

int main(void)
{
    printf("offsetof(struct test_struct, num) = %d\n",offsetof(struct test_struct, num));
    printf("offsetof(struct test_struct,  ch) = %d\n",offsetof(struct test_struct, ch));
    printf("offsetof(struct test_struct,  fl) = %d\n",offsetof(struct test_struct, fl)); 
    return 0;
}

例子输出结果:

offsetof(struct test_struct, num) = 0
offsetof(struct test_struct,  ch) = 4
offsetof(struct test_struct,  fl) = 8

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


最后通过强制类型转换(size_t)把一个地址值转换为一个整数。
分析完container_of的定义,接下来举两个例子来体会一下它的使用方法。

正确的例子,如清单2:

#include <stdio.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

/* include/linux/kernel.h:
 * 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) );})

struct student {
	char name[32];
	int age;
	double score;
};

int main(void)
{
	struct student *temp_jack;
    struct student jack = {"jack", 18, 89.4};
	int *page = &(jack.age); 
	
	temp_jack = container_of(page, struct student, age);
	
	printf("jack's name is %s\n",temp_jack->name);
	printf("jack's age is %d\n",temp_jack->age);
	printf("jack's score is %.2f\n",temp_jack->score);
	
    return 0;
}

例子输出结果:

jack's name is jack
jack's age is 18
jack's score is 89.40

本文主要内容转自:点击打开链接

更多相关文章

  1. 如果后台的SVN服务器IP地址更改了,如何修改客户端的连接url呢?
  2. Linux内核数据结构之链表
  3. Linux 下串口编程之三 termios结构体介绍
  4. Linux 修改ip地址
  5. linux配置虚拟IP地址方法
  6. 使用.NetCore在Linux上写TCP listen 重启后无法绑定地址
  7. Linux系统下用C语言获取MAC地址
  8. Red Hat Linux下如何修改网卡MAC地址
  9. 一些下载交叉编译环境的地址

随机推荐

  1. android设备连接到pc进行应用程序调试
  2. 使用Kotlin开发android学习记录(一)
  3. Android 游戏开发的一些基础和个人经验
  4. Android - 图解向 Android Studio 中导入
  5. android Glide简单使用
  6. 在Android平台上实现条型码扫描与识别
  7. Android2.3发布
  8. Android 布局之LinearLayout和RelativeLa
  9. Android 2.2新增Widget之ProtipWidget源
  10. Android启动脚本init.rc