typeof, offsetof 和container_of

要理解Linux中实现的双向循环链表("侵入式"链表),首先得弄明白宏container_of。 本文尝试从gcc的关键字typeof和宏offsetof入手,循序渐进地剖析宏container_of之实现原理。

1. typeof (from: https://en.wikipedia.org/wiki/Typeof)

typeof is an operator provided by several programming languages to
determine the data type of a variable. This is useful when
constructing programs that must accept multiple types of data
without explicitly specifying the type.

The GNU compiler (GCC) extensions for the C programming language provide
typeof:

#define max(a, b)        ({ typeof (a) _a = (a);           typeof (b) _b = (b);          _a > _b ? _a : _b; })

typeof和sizeof一样,都是关键字。只不过typeof不是标准的c语言关键字,而是gcc的c语言的扩展关键字。 typeof的作用是取得某个变量的数据类型。例如:

unsigned int a = 1; // typeof (a) is unsigned int
short b = 2;        // typeof (b) is short

2. offsetof (from: include/linux/stddef.h)

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

/*  * @TYPE: The type of the structure  * @MEMBER: The member within the structure to get the offset of  */

宏offsetof的作用是获取某个成员变量(MEMBER)在其所在的结构体(TYPE)里的偏移。 上面的宏实现得非常巧妙(如果对汇编熟悉就更容易理解),剖析如下:

(1) X = (TYPE *)0  // 将地址0x0强制转化为结构体类型TYPE的首地址
(2) Y = X->MEMBER  // 访问成员变量MEMBER
(3) Z = &Y         // 取得成员变量MEMBER的内存地址
(4) N = (size_t)Z  // 将成员变量MEMBER的内存地址强制转换成偏移量,
      = (size_t)&Y // 由于内存地址也是整数,所以成员变量MEMBER相对于结构体首地址的偏移等于&Y
      = (size_t)&(X->MEMBER) = (size_t)&X->MEMBER  // 注意: -> 比 & 优先级高
      = (size_t)&(X)->MEMBER = (size_t)&((TYPE *)0)->MEMBER

3. container_of (from: include/linux/kernel.h)

 1 /**
 2  * container_of - cast a member of a structure out to the containing structure
 3  * @ptr:        the pointer to the member.
 4  * @type:       the type of the container struct this is embedded in.
 5  * @member:     the name of the member within the struct.
 6  *
 7  */
 8 #define container_of(ptr, type, member) ({                       9         const typeof( ((type *)0)->member ) *__mptr = (ptr);    10         (type *)( (char *)__mptr - offsetof(type,member) );})
  • L9:  用一个临时变量__mptr保存成员变量的指针ptr
  • L10: offsetof(type, member): 计算出成员变量相对于其所在的结构体的偏移,不妨记为OFFSET
  • L10: __mptr - OFFSET, 就是成员变量所在的结构体的首地址

因此, 宏container_of的作用就是根据某个成员变量的内存地址,反推出其所在的结构体变量的首地址

示例代码: foo.c

 1 #include <stdio.h>
 2
 3 #define offsetof(TYPE, MEMBER)  ((size_t)&(((TYPE *)0)->MEMBER))
 4
 5 #define container_of(ptr, type, member) ({                               6                 const typeof( ((type *)0)->member ) *__mptr = (ptr);     7                 (type *)( (char *)__mptr - offsetof(type,member) );})
 8
 9 typedef struct foo_s {
10         int             m_int;
11         short           m_short;
12         char            m_char;
13         long long       m_longlong;
14 } foo_t;
15
16 int
17 main(int argc, char *argv[])
18 {
19         foo_t ox = {0x12345678, 0x1234, ‘A‘, 0xfedcba9876543210};
20         char *p3 = &ox.m_char;
21         foo_t *p = container_of(p3, foo_t, m_char);
22
23         printf("foo_t ox (%p) sizeof(foo_t) = %d\n", &ox, sizeof (foo_t));
24         printf("foo_t *p (%p) p3(%p)\n", p, p3);
25         printf("foo_t->m_int      = %#x\t\t(%d)(%p)\n",
26             p->m_int, offsetof(foo_t, m_int), &ox.m_int);
27         printf("foo_t->m_short    = %#x\t\t(%d)(%p)\n",
28             p->m_short, offsetof(foo_t, m_short), &ox.m_short);
29         printf("foo_t->m_char     = %c\t\t\t(%d)(%p)\n",
30             p->m_char, offsetof(foo_t, m_char), &ox.m_char);
31         printf("foo_t->m_longlong = %#llx\t(%d)(%p)\n",
32             p->m_longlong, offsetof(foo_t, m_longlong), &ox.m_longlong);
33
34         return 0;
35 }

编译并运行

$ gcc -g -Wall -m32 -o foo foo.c
$ ./foo
foo_t ox (0xbfdfe990) sizeof(foo_t) = 16
foo_t *p (0xbfdfe990) p3(0xbfdfe996)
foo_t->m_int      = 0x12345678          (0)(0xbfdfe990)
foo_t->m_short    = 0x1234              (4)(0xbfdfe994)
foo_t->m_char     = A                   (6)(0xbfdfe996)
foo_t->m_longlong = 0xfedcba9876543210  (8)(0xbfdfe998)

反汇编并结合gcc -E foo.c

(gdb) set disassembly-flavor intel
(gdb) disas /m main
Dump of assembler code for function main:
18      {
   0x0804841d <+0>:     push   ebp
   0x0804841e <+1>:     mov    ebp,esp
   0x08048420 <+3>:     and    esp,0xfffffff0
   0x08048423 <+6>:     sub    esp,0x40

19              foo_t ox = {0x12345678, 0x1234, ‘A‘, 0xfedcba9876543210};
   0x08048426 <+9>:     mov    DWORD PTR [esp+0x30],0x12345678
   0x0804842e <+17>:    mov    WORD PTR [esp+0x34],0x1234
   0x08048435 <+24>:    mov    BYTE PTR [esp+0x36],0x41
   0x0804843a <+29>:    mov    DWORD PTR [esp+0x38],0x76543210
   0x08048442 <+37>:    mov    DWORD PTR [esp+0x3c],0xfedcba98

20              char *p3 = &ox.m_char;
   0x0804844a <+45>:    lea    eax,[esp+0x30]
   0x0804844e <+49>:    add    eax,0x6
   0x08048451 <+52>:    mov    DWORD PTR [esp+0x24],eax

21              foo_t *p = container_of(p3, foo_t, m_char);
   0x08048455 <+56>:    mov    eax,DWORD PTR [esp+0x24]
   0x08048459 <+60>:    mov    DWORD PTR [esp+0x28],eax
   0x0804845d <+64>:    mov    eax,DWORD PTR [esp+0x28]
   0x08048461 <+68>:    sub    eax,0x6
   0x08048464 <+71>:    mov    DWORD PTR [esp+0x2c],eax
#
# --- L21‘s output from "gcc -E foo.c" ---
# 001               foo_t *p = ({
# 002    const typeof( ((foo_t *)0)->m_char ) *__mptr = (p3);
# 003    (foo_t *)( (char *)__mptr - ((size_t)&(((foo_t *)0)->m_char)) );
# 004               });
#

分析(TBD)

小结(TBD)

时间: 2024-11-21 06:44:07

typeof, offsetof 和container_of的相关文章

linux中offsetof与container_of宏定义

linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER)    ((size_t) &((TYPE *)0)->MEMBER) /** * container_of - cast a member of a structure out to the containing structure * @ptr:        the pointer to the member. * @type:       the 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) );}) 原文地

typeof、offsetof、container_of的解释

链表是内核最经典的数据结构之一,说到链表就不得不提及内核最经典(没有之一)的宏container_of. container_of似乎就是为链表而生的,它的主要作用是根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针,最典型的应用就是根据链表节点获取链表上的元素对象. container_of的宏定义如下: #define container_of(ptr, type, member) ({            \ const typeof( ((type *)0)->m

typeof、offsetof、container_of

typeof 用于获取一个对象的类型,比如: unsigned int a = 1; // typeof (a) is unsigned int short b = 2; // typeof (b) is short offsetof #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) 获取某个成员变量(MEMBER)在其所在的结构体(TYPE)里的偏移: 首先将地址0强行转换为 TYPE * 类型 再将此对象中的MEM

C语言笔记(结构体与offsetof、container_of之前的关系)

关于结构体学习,需要了解:结构体的定义和使用.内存对齐.结构体指针.得到结构体元素的偏移量(offsetof宏实现) 一.复习结构体的基本定义和使用 1 typedef struct mystruct 2 { 3 int a; 4 char b; 5 double c; 6 }MyS1; 7 8 /* 9 函数功能:演示结构体的定义和使用 10 */ 11 void func1(void) 12 { 13 //定义时赋值 14 MyS1 s1 = { 15 .a =1, 16 .b =2, 17

对offsetof、 container_of宏和结构体的理解

offsetof 宏 #include<stdio.h> #define offsetoff(type, member)      ((int)&((type*)0)->member) /* ((type*)0)->member 释义:声明一个相应类型的结构体指针,该指针指向0地址处.再通过该指针访问各元素.我们只要获取一个指针就能访问其内部的元素吗,可以这么搞?其实我是想联系全局和非全局变量,通过上面这个代码也许你不明白我要表达的意思.请继续慢慢看,直到本文后面,结合本文

offsetof与container_of宏分析

offsetof宏:结构体成员相对结构体的偏移位置 container_of:根据结构体成员的地址来获取结构体的地址 offsetof 宏 原型: #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) (TYPE *)0非常巧妙,告诉编译器有一个指向结构体 TYPE 的指针,其地址是0,然后取该指针的 MEMBER 地址 &((TYPE *)0)->MEMBER,因为基址是0,所以这时获取到的 MEMBER

container_of()详解

?原地址:http://radek.io/2012/11/10/magical-container_of-macro/ 当你开始内核编程的时候,你会随便看看代码,那么你可能很快就会碰到这个神奇的预处理代码结构. 它是用来干嘛的?正如它的名称那样,它获取的是它的容器的地址.该函数需要三个变量--指针,容器的类型,指向成员的指针.(译者注:最终返回的是,该成员所处的结构体的指针.知道成员的指针,根据结构体的size就可以算出来了,具体的算法还要看下面)该宏将返回包含对应成员的容器的指针地址.它确实是

Linux内核数据结构——链表

目录 目录 简介 单向链表 双向链表 环形链表 Linux内核中的链表实现 offsetof container_of container_of 第一部分 container_of 第二部分 链表初始化 向链表中增加一个节点 删除节点 移动节点 判断链表是否为空 遍历链表 Demo测试 mlisth mlistc 执行结果 简介 最近在学习Android Binder驱动程序实现的时候,发现里面的数据结构用到了struct list_head.而我google发现struct list_head