C语言链表中list函数,内核链表中list_entry()函数 以及typeof的用法详解

一、内核链表中list_entry()函数的解读

在看内核链表这部分的时候看到list_entry,查看其代码发现就是前面

#define list_entry(ptr, type, member) \ container_of(ptr, type, member)

所以在这分析一下container_of宏和其中的offsetof宏

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

宏功能:获得一个结构体变量成员在此结构体中的偏移量。

1. ( (TYPE *)0 ) 将零转型为TYPE类型指针;

2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;

3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址,即相对于0的偏移量,要的就这个;

4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型,size_t应该最终为unsigned int类型。

此宏的巧妙之处在于将 0 转换成(TYPE*),这样结构体中成员的地址即为在此结构体中的偏移量。

示例:

#include 

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

struct_test_{

intx;

inty;

floatz;

};

intmain(void)

{

inttemp = -1;

temp = offsetof(struct_test_, z);

printf("temp = %d\n", temp);

return0;

}

运行后结构为:temp = 8。

显然求出了 结构体成员变量 z 在结构体中的偏移量为 8。

2、#define container_of(ptr, type, member) ({ \

const typeof( ((type *)0)->member ) *__mptr = (ptr); \

(type *)( (char *)__mptr - offsetof(type,member) );})

宏功能:从结构体(type)某成员变量(member)指针(ptr)来求出该结构体(type)的首指针。

这里比较啰嗦,举个例子吧,然后分析:

#include 

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

#define container_of(ptr, type, member) ({   \

consttypeof( ((type *)0)->member ) *__mptr = (ptr); \

(type *)( (char*)__mptr - offsetof(type,member) );})

struct_test_

{

intx;

inty;

intz;

};

voidAssignment(struct_test_ *t)

{

t->x = 1;

t->y = 2;

t->z = 3;

}

voidGetheadPoint(int*tz)

{

struct_test_ *p;

inttemp = -1;

p = container_of(tz,struct_test_, z);//根据成员变量的地址获得该结构体的首地址

temp = p->y;                             //根据首地址获得其中另外一个成员变量的值

printf("line31 = %d\n", temp);

}

intmain(void)

{

inttemp = -1;

struct_test_ tmp;//定义一个结构体变量

Assignment(&tmp);                        //给这个变量赋值

GetheadPoint(&tmp.z);                    //只传给这个函数一个结构体成员变量的地址

printf("line43 tmp - >x = %d\n", tmp.x);

return0;

}

运行结果为:

line31 = 2

line43 tmp - >x = 1

大概解释一下:先定义一个结构体并给其赋值,现在 GetheadPoint函数只传入这个结构体的一个子成员变量地址。在 GetheadPoint函数中通过container_of宏来获得此结构体的首指针,通过此首指针即可以获得此结构体的其它子成员的值。

1、typeof( ( (type *)0)->member )为取出member成员的变量类型(请参考后面对typeof的用法说明)。

2、定义__mptr指针ptr为指向该成员变量的指针

3、mptr为member数据类型的常量指针,其指向ptr所指向的变量处

4、.(char *)__mptr转换为字节型指针。(char *)__mptr - offsetof(type,member))用来求出结构体起始地址(为char *型指针),然后(type *)( (char *)__mptr -offsetof(type,member) )在(type *)作用下进行将字节型的结构体起始指针转换为type *型的结构体起始指针。

5、.({ })这个扩展返回程序块中最后一个表达式的值。

二、typeof关键字

container_of(ptr, type, member)中包含typeof关键字,我们在这分析一下typeof

typeof关键字是C语言中的一个新扩展。只要可以接受typedef名称,Sun Studio C 编译器就可以接受带有typeof的结构,包括以下语法类别:

声明

函数声明符中的参数类型链表和返回类型

类型定义

类型操作符s

sizeof操作符

复合文字

typeof实参

编译器接受带双下划线的关键字:__typeof和__typeof__。本文中的例子并没有遵循使用双下划线的惯例。从语句构成上看,typeof关键字后带圆括号,其中包含类型或表达式的名称。这类似于sizeof关键字接受的操作数(与sizeof不同的是,位字段允许作为typeof实参,并被解释为相应的整数类型)。从语义上看,typeof 关键字将用做类型名(typedef名称)并指定类型。

1、使用typeof的声明示例

下面是两个等效声明,用于声明int类型的变量a。

typeof(int) a; /* Specifies variable a which is of the type int */

typeof('b') a; /* The same. typeof argument is an expression consisting of

character constant which has the type int */

以下示例用于声明指针和数组。为了进行对比,还给出了不带typeof的等效声明。

typeof(int *) p1, p2; /* Declares two int pointers p1, p2 */

int *p1, *p2;

typeof(int) * p3, p4;/* Declares int pointer p3 and int p4 */

int * p3, p4;

typeof(int [10]) a1, a2;/* Declares two arrays of integers */

int a1[10], a2[10];

如果将typeof用于表达式,则该表达式不会执行。只会得到该表达式的类型。以下示例声明了int类型的var变量,因为表达式foo()是int类型的。由于表达式不会被执行,所以不会调用foo函数。

extern int foo();

typeof(foo()) var;

2、使用typeof的声明限制

请注意,typeof构造中的类型名不能包含存储类说明符,如extern或static。不过允许包含类型限定符,如const或volatile。例如,下列代码是无效的,因为它在typeof构造中声明了extern:typeof(extern int) a;

下列代码使用外部链接来声明标识符b是有效的,表示一个int类型的对象。下一个声明也是有效的,它声明了一个使用const限定符的char类型指针,表示指针p不能被修改。extern typeof(int) b;

typeof(char * const) p = "a";

3、在宏声明中使用typeof

typeof构造的主要应用是用在宏定义中。可以使用typeof关键字来引用宏参数的类型。因此,在没有将类型名明确指定为宏实参的情况下,构造带有所需类型的对象是可能的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值