Linux内核中两个宏分析

Linux 内核中常用的两个宏

// 第一个宏
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#endif

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

 

offsetof宏分析:

offsetof 用于计算 TYPE 结构体中 MEMBER 成员的偏移量。

 ((size_t)&((TYPE*)0)->MEMBER)

直接将0强制转换为结构体指针类型, 这样直接使用0地址处不会导致程序崩溃吗?

后续将对这个进行解释。

 

编译器清楚的知道结构体成员变量的偏移位置。

通过结构体变量的首地址与偏移量定位成员变量。

struct ST
{
   int i;    // 0
   int j;    // 4
   char c;   // 8
};


void func(struct ST* pst)
{
    int* pi = &(pst->i);    //  0  (unsigned int)&s + 0
    int* pj = &(pst->j);    //  4  (unsigned int)&s + 4
    char* pc = &(pst->c);   //  8  (unsigned int)&s + 8

    printf("pst = %p\n", pst);
    printf("pi = %p\n", pi);
    printf("pj = %p\n", pj);
    printf("pc = %p\n", pc);
}

int main()
{
    struct ST s = {0};

    func(&s);
    func(NULL); 


    return 0;
}

输出结果:

 

func(NULL); 

用空指针没有报错可分析得:

编译器根本没有去访问 s 地址(NULL地址或者 0地址)对应内容并进行访问工作,只是用这个地址加上偏移量即做加法运算,所以就不会有奔溃的问题。

这个也解释宏设计中offsetof 使用 0 地址为什么不会崩溃的问题。


(TRPE* 0)->MENBER ===>>>> 0地址 + MENBER 的偏移量
#include <stdio.h>

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


struct ST
{
    int i;     // 0
    int j;     // 4
    char c;    // 8
};

int main()
{
    struct ST s = {0};

    printf("offset i: %d\n", offsetof(struct ST, i));
    printf("offset j: %d\n", offsetof(struct ST, j));
    printf("offset c: %d\n", offsetof(struct ST, c));

    return 0;
}

 

 

 

container_of宏分析:

一:宏定义中 ({})

它是 GUN C 编译器扩展语法。

它与逗号表达式类似,结果为最后一个语句的值。

 

二:typeof 关键字

typeof 是 GNU C编译器的特有关键字。

它只在编译器生效,用于得到变量的类型。

    int i = 100;
    typeof(i) j = i;
    const typeof(j)* p = &j;

    printf("sizeof(j) = %d\n", sizeof(j));
    printf("j = %d\n", j);
    printf("*p = %d\n", *p);

三:原理(指针运算)

强制转换的目的就是 char* 是为了做指针运算

 

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

struct ST
{
    int i;     // 0
    int j;     // 4
    char c;    // 8
};

int main()
{

    struct ST s = {0};
    char* pc = &s.c;

    struct ST* pst = container_of(pc, struct ST, c);
	
    printf("&s = %p\n", &s);
    printf("pst = %p\n", pst);

    return 0;
}

结果发现两个地址一样,说明通过  container_of()  可以获得结构体的起始地址。

 

注意:

这个container_of宏是在GUN C 中才能使用,因为 (typeof关键字)是它编译器特有的,如果想在标准C中使用需要裁减新的宏,但是没办法检查类型是否正确。

#ifndef container_of_new
#define container_of_new(ptr, type, member) ((type*)((char*)(ptr) - offsetof(type, member)))
#endif

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值