• LinuxKernel(一)


    首先,回顾一下基础的宏操作:

    C语言宏

    ###

    1. #的作用是字符串化:在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组

      #define ERROR_LOG(info) fprintf(stderr,"error:"#info"
      ");
      

      则有:

      ERROR_LOG("add"); ---> fprintf(stderr,"error: "add"
      ");
      ERROR_LOG(devied =0); ---> fprintf(stderr,"error: devied=0
      ");
      
    2. #是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。

      例如:

      #define XNAME(n) x##n
      

      那么XNAME(4)就会展开为x4.

    do{/*codes*/}while(0)

    采用这种方式是为了防范在使用宏过程中出现错误,主要有如下几点:

      (1)空的宏定义避免warning:

    #define foo() do{}while(0)
    

      (2)存在一个独立的block,可以用来进行变量定义,进行比较复杂的实现。
      (3)如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现:

    #define foo() 
    	action1(); 
    	action2();
    

      在遇到分支语句时:

    if(NULL == pPtr)
      foo();
    

    foo()中的两个语句就不会都被执行。

    (4)为何不用单独的{}

    #define switch(x,y) {int tmp; tmp=x;x=y;y=tmp;}
    if(x>y)
     switch(x,y);
    else
     op();
    

    在把宏引入代码中,会多出一个分号,从而会报错。

    变参宏: ···__VA_ARGS__

    某些函数接受可变的参数例如printf(),在头文件stdvar.h中有工具可以自定义变参宏。

    把宏参数列表中最后的参数用···省略,而__VA_ARGS__可用在替换部分,表面省略号代表的东西。

    #define PR(···)  printf(__VA_ARGS__)
    

    例如:

    PR("THIS IS __VA_ARGS__");
    

    会被展开为:

    printf("THIS IS __VA_ARGS__");
    

    预定义符号

    符号 样例值 含义
    __FILE__ "test.c" 进行编译的文件名
    __LINE__ 25 当前行的行号
    __DATE__ "Jan 31 2001" 被编译的日期
    __TIME__ "23:17:24" 被编译的时间
    __STDC__ 1 是否遵循ANSI C
    __FUNCTION__ main 所在函数名称

    这些宏与编译器有关,有些支持有些不支持.

    如下程序:

    h1.png

    运行结果为:

    h2.png

    注意到: 如果用函数或内联函数,每次的行号便都会相同。

    下面是内核中,常见的两个宏:

    Linux常用的两个宏

    offsetof

    该宏的定义如下:

    #define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member )
    

    作用是获取结构体某成员变量的偏移量。

    分析如下:

    1. (type *)0 将0转化为该类型的指针,即地址为0x00000000
    2. ((type *)0)->member 访问成员member
    3. &(((type *)0)->member) 获取该成员地址(也就是其偏移量)
    4. (size_t)&(((type *)0)->member) 将地址转化为size_t类型 即偏移量

    这里访问0指针为何不会报错,取决于gcc对于该过程的优化,不会直接访问空间而是直接获得地址.

    container_of

    该宏的定义如下:

    #define __container_of__(ptr, type, member) ({
                    const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);
                    ( type * )( ( char * )__mptr - __offsetof__( type, member ) );
                    })
    

    要理解这段宏,需要知道几个GCC C EXTENSIONS,查阅GCC MANUAL:

    1.Statements and Declarations in Expressions
    m1.png
    3.JPG

    2.Referring to a Type with typeof

    m2.png
    手册中也给出了一个典型的用法示例:

    m3.png

    这段宏的分析如下:

    1. typeof()GNU C ,获得变量类型
    2. typeof (((type *)0 )->member) 起始地址为0 再获取member 最后返回member类型
    3. const typeof (((type *)0 )->member) * __mptr=(ptr) 定义 __mptr 指针,指向ptr指向的地址,并成为常量指针
    4. (char *)__mptr __mptr转化为字符型指针(运算以1个字节为单位)
    5. - __offsetof__(type,member)) 减去该成员的偏移量
    6. (type*)( ( char * )__mptr - __offsetof__(type,member)) 最后转化为指向该类型的指针(指向该类型的首地址)

    关于上述两个宏的一段程序如下:

    #include <stdio.h>
    #include <string.h>
    
    /**
     * 获取结构体变量成员的偏移量
     * @param type 类型(struct)
     * @param member 成员
     */
    #define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member )
    
    /**
     * 获取指向整个结构体的指针
     * @param ptr 指向成员(member)变量的指针
     * @param type 类型(struct)
     * @param member 成员变量
     */
    #define __container_of__(ptr, type, member) ({
                    const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);
                    ( type * )( ( char * )__mptr - __offsetof__( type, member ) );
                    })
    
    typedef struct Student {
        char gender;
        int id;
        int age;
        char name[20];
        double score;
    } Stu;
    
    int main() {
        int gender_offset,id_offset,age_offset,name_offset,score_offset;
        gender_offset = __offsetof__(struct Student, gender);
        id_offset     = __offsetof__(struct Student, id);
        age_offset    = __offsetof__(struct Student, age);
        name_offset   = __offsetof__(struct Student, name);
        score_offset  = __offsetof__(struct Student, score);
        printf("%d	%d	%d	%d	%d
    ", gender_offset, id_offset, age_offset, name_offset, score_offset);
    
        Stu stu;
        Stu *pstu;
        stu.gender = '1';
        stu.id = 9527;
        stu.age = 18;
        stu.score = 98.2;
        strcpy(stu.name, "elioyang");
    
        pstu = __container_of__(&stu.id, Stu, id);
    
        printf("gender=%c
    ", pstu->gender);
        printf("age=%d
    ", pstu->age);
        printf("name=%s
    ", pstu->name);
        printf("score=%lf", pstu->score);
    
        return 0;
    }
    

    运行结果如下:

    0       4       8       12      32
    gender=1
    age=18
    name=elioyang
    score=98.200000
    
  • 相关阅读:
    对于global的介绍
    elang和python互通的例子
    gen_server的一些心得
    erlang分布式例子
    erlang热部署
    erlang配置三方库
    opencv学习之PyImageSearch
    雅克比矩阵
    罗德里格斯公式的证明
    opencv学习网站
  • 原文地址:https://www.cnblogs.com/oasisyang/p/13374833.html
Copyright © 2020-2023  润新知