C语言中#和##的作用

  1. C语言中#和##的作用
  2. 提供代码都是可以正常运行起来
    

###符号是C和C++的宏(macro)属于编译器预处理的范畴,编译期间完成

  1. #的功能是将其后面的宏参数进行字符串化操作;#符号是把传递过来的参数多做字符串进行处理;简单说就是在对它所引用宏参数在其左右加一个双引号,示例代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>

    #define str(param) #param //#将param参数经行字符串化

    int main()
    {
    printf(str(hello 122 333444\n));
    return 0;
    }
    输出结果:
    1
    hello 122 333444

  1. ##符号称为连接符,将两个或多个标记连接为一个合法的标识符;代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    /* ## */
    #define var(x) var##x
    int main()
    {
    int var(1) = 1;
    int var(2) = 2;
    int var(3) = 3;

    printf("var2 : %d\n", var2);
    return 0;
    }

    运行结果:

    1
    var2 : 2
  2. 比如要做一个菜单项由变量和函数指针组成的结构体数组,并且希望在函数名和菜单项命名之间有直观的名字上的关系,那么下面的代码就非常实用:可能在有些规则编码中是不允许使用的,因为这样,没有明确的关系,难以查找。这里只是总结一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    #include <stdio.h>

    #define barrier(x) barrier##x
    #define COMMAND(NAME) {NAME , NAME##_command}

    /**/
    struct command
    {
    char *name;
    void(*function)(void);
    };

    /**
    * @brief 建议在两个需要都被打印的数组中添加一个变量,比如下面所示;
    * 或者可以指定printf打印字符串长度,至于原因,自己想想
    */
    char quit[4] = {"quit"};
    char barrier(0) = 0; /*阻隔*/
    char help[4] = {"help"};
    void quit_command(void);
    void help_command(void);

    /*在这里已经对变量赋值和函数指针赋值,展开效果:
    * struct command commands[] = {{quit quit_command},{help help_command}};
    */
    struct command commands[] = { COMMAND(quit) ,COMMAND(help)};

    void quit_command(void)
    {
    printf("quit_command name: %-4.4s\n", commands[0].name);
    /*-4.4s:关于这个可以在本博客中搜索 "printf打印指定长度字符串" */
    }

    void help_command(void)
    {
    printf("help_command name: %s\n", commands[1].name);
    }

    int main()
    {
    commands[0].function();
    commands[1].function();
    return 0;
    }

    运行结果:

    1
    2
    quit_command name: quit
    help_command name: help

    COMMAND宏在这里充当一个代码生成器的作用,这样可以在一定程度上减少代码密度,间接地也可以减少不留心造成地错误,

  3. 我们还可以n个##连接n+1个字符串,这个也是#符号不具备地,比如:

    1
    2
    3
    4
    5
    6
    #define LINK_MULT(a,b,c,d)				a##_##b##_##c##_##d
    typedef struct _record_type LINK_MULT(name,Company,Position,salay);
    /*
    * 语句展开效果:
    * typedef struct _record_type typedef struct _record_type
    */
  4. ##功能是在带参的宏定义中,将两个字符串连接起来,形成一个新的子串;
    假设程序中已经定义这样一个带参的宏

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #define poster(n)		printf("t"#n"=%d\n",t##n);
    /*同时又定义了一个整形变量*/
    int t9 = 9;
    int main()
    {
    /*在编译时,poster(9)扩展为 printf("t""9""=%d\n",t9);*/
    poster(9);
    return 0;
    }

    运行结果:

    1
    t9=9

    注意在这个例子中,
    上面的是一般用法


  1. 当宏参数是另一个宏的时候,需要注意的是凡宏定义里有用#和##的地方,宏参数是不会再展开 非###`的情况:
1
2
3
4
5
#define TOW					(2)
#define MUL(a,b) (a*b)
printf("%d * %d = %d\n",TOW,TOW,MUL(TOW,TOW));
这行的宏展开为:
printf("%d * %d = %d\n",2,2,MUL(2,2));
  1. 当有###的时候
    1
    2
    3
    4
    5
    6
    7
    #define A 			2
    #define STR(s) #s
    #define CONS(a,b) (int)(a##e##b)
    printf("int max:%s\n", STR(INTPTR_MAX)); //INTPTR_MAX =》stdint.h
    //展开 printf("int max:%s\n", "INTPTR_MAX"); //INTPTR_MAX =》stdint.h
    printf("%s\n",CONS(A,A)); //COMPILE ERROR
    //展开 printf("%s\n","AeA");
    INTPTR_MAX和A都不会展开然而解决这个问题方法很多,加一层中间转换宏,加这层宏的用意是把所有宏的参数在这层里全部展开,那么在转换宏里的那一个宏就能得到正确的宏参数。
    1
    2
    3
    4
    5
    6
    7
    8
    #define A 			2
    #define _STR(s) #s
    #define STR(s) _STR(s) //转换宏
    #define _CONS(a,b) (int)(a##e##b)
    #define CONS(a,b) _CONS(a,b) //转换宏

    printf("int max:%s\n", STR(INTPTR_MAX)); //INTPTR_MAX =》stdint.h
    printf("CONS: %d\n",CONS(A,A)); //COMPILE ERROR
    输出结果:
    1
    2
    int max:2147483647i32
    CONS: 200

  1. ###的一些应用,合并匿名创建变量名
    1
    2
    3
    4
    5
    #define _ANONYMOUS1(type,var ,line)			type var##line
    #define _ANONYMOUS0(type,line) _ANONYMOUS1(type,anonymous ,line)
    #define ANONYMOUS0(type) _ANONYMOUS0(type,__LINE__)

    ANONYMOUS0(static int) = 100;
  2. 填充结构体
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    #include <stdio.h>

    #define barrier(x) barrier##x
    #define COMMAND(NAME) {NAME , NAME##_command}

    /**/
    struct command
    {
    char *name;
    void(*function)(void);
    };

    /**
    * @brief 建议在两个需要都被打印的数组中添加一个变量,比如下面所示;
    * 或者可以指定printf打印字符串长度,至于原因,自己想想
    */
    char quit[4] = {"quit"};
    char barrier(0) = 0; /*阻隔*/
    char help[4] = {"help"};
    void quit_command(void);
    void help_command(void);

    /*在这里已经对变量赋值和函数指针赋值,展开效果:
    * struct command commands[] = {{quit quit_command},{help help_command}};
    */
    struct command commands[] = { COMMAND(quit) ,COMMAND(help)};

    void quit_command(void)
    {
    printf("quit_command name: %-4.4s\n", commands[0].name);
    /*-4.4s:关于这个可以在本博客中搜索 "printf打印指定长度字符串" */
    }

    void help_command(void)
    {
    printf("help_command name: %s\n", commands[1].name);
    }

    int main()
    {
    commands[0].function();
    commands[1].function();
    return 0;
    }
    运行结果:
    1
    2
    quit_command name: quit
    help_command name: help
  3. 记录文件名
    1
    2
    3
    4
    #define _GET_FILE_NAME(f)					#f
    #define GET_FILE_NAME(f) _GET_FILE_NAME(f)
    char file_name[] = { GET_FILE_NAME(__FILE__)};
    printf("file_name %s\n", file_name);
  4. 得到一个数值类型所对应的字符串缓冲大小
    1
    2
    3
    4
    #define _TYPE_BUF_SIZE(type)		sizeof(#type)
    #define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)
    char buf[TYPE_BUF_SIZE(INT32_MAX)] = { GET_FILE_NAME (INT32_MAX) };
    printf("buf %s\n", buf);