• 第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 = %p
    ", pst);
        printf("pi = %p
    ", pi);
        printf("pj = %p
    ", pj);
        printf("pc = %p
    ", pc);
    };
    
    int main()
    {
        struct ST s = {0};
        
        func(&s);
        func(NULL);//借用0地址计算成员变量的偏移地址,相当于pst=0
        
        printf("offset i: %d
    ", offsetof(struct ST, i)); //0
        printf("offset j: %d
    ", offsetof(struct ST, j)); //4
        printf("offset c: %d
    ", 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编译器的语法扩展

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

     

    (3)typeof关键字

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

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

    (4)container_of原理

     

    【编程实验】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 = %d
    ", c);
        printf("d = %d
    ", d);    
    }
    
    //typeof关键字:GNU C的扩展关键字
    void type_of()
    {
        int i = 100;
        typeof(i) j = i;
        const typeof(j)* p = &j;
        
        printf("sizeof(j)= %d
    ", sizeof(j));
        printf("j = %d
    ", j);
        printf("*p = %d
    ", *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 = %p
    ", &s);
        printf("pst = %p
    ", 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使用({})进行类型安全检查

  • 相关阅读:
    【转】几种Java序列化方式的实现
    【转】Java泛型方法
    【转】java序列化一定要应该注意的6个事项!
    [转]Android APK签名原理及方法
    [转]Android中内存占用的含义:(VSS,PSS,RSS,USS)
    红黑树的C语言实现
    Btree算法的C语言实现
    C++之迭代器失效总结
    tcpdump抓包工具用法说明
    setsockopt函数功能及参数详解
  • 原文地址:https://www.cnblogs.com/5iedu/p/7148969.html
Copyright © 2020-2023  润新知