第31课 老生常谈的两个宏(linux)

1. Linux内核中常用的两个宏定义

(1)offsetof宏:用于计算TYPE结构体中MEMBER成员的偏移位置

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

(2)container_of宏:根据成员变量指针反推结构体对象的起始地址

//const typeof(...):编译期类型检查,const指针兼容const和非const指针类型 
#ifndef container_of
#define container_of(ptr, type, member) ({                 
        const typeof(((type*)0)->member)* __mptr = (ptr);  
        (type*)((char*)__mptr - offsetof(type, member));})
#endif

2. 原理剖析

(1)巧用0地址

  ①编译器清楚的知道结构体成员变量的偏移地址

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

【编程实验】offsetof宏

//offsetof.c

#include "stdio.h"

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

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

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

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

    func(&s);
    func(NULL);//借用0地址计算成员变量的偏移地址,相当于pst=0
    
    printf("offset i: %dn", offsetof(struct ST, i)); //0
    printf("offset j: %dn", offsetof(struct ST, j)); //4
    printf("offset c: %dn", offsetof(struct ST, c)); //8

    return 0;
}
/*输出结果
pst = 0029FEA4
pi = 0029FEA4
pj = 0029FEA8
pc = 0029FEAC
pst = 00000000
pi = 00000000
pj = 00000004
pc = 00000008
offset i: 0
offset j: 4
offset c: 8
*/

(2)({})是何方神圣

  ①它是GNU C编译器的语法扩展

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

第31课 老生常谈的两个宏(linux) 

(3)typeof关键字

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

  ②typeof只在编译期生效,用于得到变量的类型

(4)container_of原理

第31课 老生常谈的两个宏(linux) 

【编程实验】container_of原理

//container_of.c

#include "stdio.h"

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

//const typeof(...):编译期类型检查,const指针兼容const和非const指针类型 
#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;  //offset 0
    int  j;  //offset 4
    char c;  //offset 8
};

//({})是何方神圣
void func()
{
    int a = 0;
    int b = 0;
    int c = (a=1, b=2, a+b); //括号表达式
    int d = ({int a=1; int b=2; a+b;}); //({})表达式,其中的{}表示一个作用域,{}外再加上()表示取最后一个语句的值
                                        //功能上类似于逗号表达式,这是GNU C扩展语法
    printf("c = %dn", c);
    printf("d = %dn", d);    
}

//typeof关键字:GNU C的扩展关键字
void type_of()
{
    int i = 100;
    typeof(i) j = i;
    const typeof(j)* p = &j;

    printf("sizeof(j)= %dn", sizeof(j));
    printf("j = %dn", j);
    printf("*p = %dn", *p);
}

int main()
{
    func();
    type_of();

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

    int e = 0;
    int* pe = &e; 

    struct ST* pst = container_of(pc, struct ST, c);
    //struct ST* pst = container_of(pe, struct ST, e); //类型检查,编译不过    
    printf("&s = %pn", &s);
    printf("pst = %pn", pst); //pst == &s;

    return 0;
}
/*输出结果
c = 3
d = 3
sizeof(j)= 4
j = 100
*p = 100-
&s = 0029FE94
pst = 0029FE94
*/

3. 小结

(1)编译器清楚地知道结构体成员变量的偏移位置

(2)({})与逗号表达式类似,结果为最后一个语句的值

(3)typeof只在编译期生效,用于得到变量的类型

(4)container_of使用({})进行类型安全检查

原文链接: https://www.cnblogs.com/5iedu/p/7148969.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    第31课 老生常谈的两个宏(linux)

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/393193

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月3日 下午3:07
下一篇 2023年4月3日 下午3:07

相关推荐