• C语言中宏的应用


    一、前言

      在后台程序运行出问题时,C语言提供很多的预处理触发点文件名、行号、函数名的方法,关键是利用C99新增的预处理标识符__VA_ARGS__;先介绍几个编译器内置的宏定义,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。

    二、ANSI C标准宏

    __LINE__    // 在源代码中插入当前源代码行号
    __FILE__    // 在源文件中插入当前源文件名
    __DATE__    // 在源文件中插入当前的编译日期
    __TIME__    // 在源文件中插入当前编译时间
    __STDC__    // 当要求程序严格遵循ANSI C标准时该标识被赋值为1
    
    __VA_ARGS__ // 是一个可变参数的宏,这个可宏是新的C99规范中新增的,
                // 目前似乎gcc和VC6.0之后的都支持(VC6.0的编译器不支持)。
                // 宏前面加上##的作用在于,可以接受参数为0个或者多个

    三、实例

    宏实例:

     1 #include <stdio.h>               
     2                                  
     3 int main()                       
     4 {                                
     5     printf("__func__:%s\n", __func__);
     6     printf("__FILE__:%s\n", __FILE__);
     7     printf("__DATE__:%s\n", __DATE__);
     8     printf("__TIME__:%s\n", __TIME__);
     9     printf("__LINE__:%d\n", __LINE__);
    10                                  
    11     return 0;                    
    12 } 

    宏实例程序输出如下:

    1 请按 ENTER 或其它命令继续
    2 __func__:main
    3 __FILE__:test1.c
    4 __DATE__:Apr 17 2022
    5 __TIME__:22:43:02
    6 __LINE__:9

    四、#和##运算符

    其中#和##运算符的功能有所不同,在这里也做一定的介绍

    1. #用来把参数转换成字符串

    实例1:

     1 #include <stdio.h>
     2 
     3 #define P(A) printf("%s:%d\n", #A, A);
     4                                                                          
     5 int main()
     6 {   
     7     int a = 2, b = 4;
     8     P(a);
     9     P(b);
    10     P(a+b);
    11     
    12     return 0;
    13 }

    实例1程序输出如下:

    1 请按 ENTER 或其它命令继续
    2 a:2
    3 b:4
    4 a+b:6

    实例2:

     1 #include <stdio.h>
     2 
     3 #define SQUARE(x) printf("The square of "#x" is %d.\n", ((x)*(x)))
     4                                                                          
     5 int main()
     6 {   
     7     SQUARE(9);
     8     
     9     return 0;
    10 }

    实例2程序输出如下:

    1 请按 ENTER 或其它命令继续
    2 The square of 9 is 81.

    2. ##运算符可以用于宏函数的替换部分

    ##就是个粘合剂,将前后两部分粘合起来,也就是有“组成变量名”的意思。特别要和#运算符的功能区分开来,#是连接字符串,而##是连接变量名。

    但是“##”不能随意粘合任意字符,必须是合法的C语言标示符。在单一的宏定义中,最多可以出现一次“#”或“##”预处理操作符。如果没有指定与“#”或“##”预处理操作符相关的计算次序,则会产生问题。为避免该问题,在单一的宏定义中只能使用其中一种操作符(即,一份“#”或一个“##”,或都不用)。除非非常有必要,否则尽量不要使用“#”和“##”。

    实例程序:

     1 #include <stdio.h>
     2 
     3 #define XNAME(n) SYSTEML_ ## n
     4 
     5 int main()
     6 {
     7     int SYSTEML_ = 0,
     8         SYSTEML_OPEN = 1,
     9         SYSTEML_WRITE = 2,
    10         SYSTEML_CLOSE = 3;
    11 
    12     printf("%d\n", XNAME());
    13     printf("%d\n", XNAME(OPEN));
    14     printf("%d\n", XNAME(WRITE));
    15     printf("%d\n", XNAME(CLOSE));
    16 
    17     return 0;
    18 } 

    实例程序输出:

    1 请按 ENTER 或其它命令继续
    2 0
    3 1
    4 2
    5 3

    此外,__VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持。实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。

    ##__VA_ARGS__ 宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的逗号去掉的作用,否则会编译出错,相当于能够接受0个及以上的参数。

    ##__VA_ARGS__的实例:

    1  1 #define my_print1(fmt,...) printf(fmt, __VA_ARGS__)                      
    2  2 my_print1("i=%d,j=%d\n",i,j) // 可以打印
    3  3 
    4  4 #define my_print2(fmt,...) printf(fmt, ##__VA_ARGS__)
    5  5 my_print2("iiijjj\n")        // 可以打印
    6  6 my_print2("i=%d,j=%d\n",i,j) // 也可以打印 
    最终的代码为:
     1 #include <stdio.h>
     2 
     3 //用#把参数转成字符串
     4 #define P(A) printf("%s: %d\n", #A, A)
     5 
     6 #define SQUARE(x) printf("The square of "#x" is %d.\n", ((x)*(x)))
     7 
     8 #define XNAME(n) SYSTEML_ ## n
     9 
    10 #define my_print1(fmt, ...) printf(fmt, __VA_ARGS__)
    11 #define my_print2(fmt, ...) printf(fmt, ##__VA_ARGS__)
    12 
    13 int main(int argc, char **argv)
    14 {
    15     //printf("Hello world!\n");
    16     printf("__func__: %s\n", __func__);
    17     printf("__FILE__: %s\n", __FILE__);
    18     printf("__DATE__: %s\n", __DATE__);
    19     printf("__TIME__: %s\n", __TIME__);
    20     printf("__LINE__: %d\n", __LINE__);
    21 
    22     int a = 2;
    23     int b = 4;
    24     P(a);
    25     P(b);
    26     P(a+b);
    27 
    28     SQUARE(9);
    29 
    30     int SYSTEML_ = 0;
    31     int SYSTEML_OPEN = 1;
    32     int SYSTEML_WRITE = 2;
    33     int SYSTEML_CLOSE = 3;
    34 
    35     printf("%d\n", XNAME());
    36     printf("%d\n", XNAME(OPEN));
    37     printf("%d\n", XNAME(WRITE));
    38     printf("%d\n", XNAME(CLOSE));
    39 
    40     int i = 3;
    41     int j = 6;
    42 
    43     my_print1("i = %d, j = %d\n", i, j);
    44     my_print2("%d%d%d%d%d%d\n", i,i,i,j,j,j);
    45     my_print2("444888\n");
    46 
    47     return 0;
    48 }   

    程序都很简单,不多说了,自己看吧

     1 #include <stdio.h>
    2  
    3 #define XNAME(n) SYSTEML_ ## n
    4  
    5 int main()
    6 {
    7     int SYSTEML_ = 0,
    8         SYSTEML_OPEN = 1,
    9         SYSTEML_WRITE = 2,
    10         SYSTEML_CLOSE = 3;
    11  
    12     printf("%d\n", XNAME());
    13     printf("%d\n", XNAME(OPEN));
    14     printf("%d\n", XNAME(WRITE));
    15     printf("%d\n", XNAME(CLOSE));
    16  
    17     return 0;
    18 }
  • 相关阅读:
    事件处理
    模板语法
    计算属性和侦听器
    Class 与 Style绑定
    Springboot使用redis
    修改docker-toolbox/boot2docker容器镜像
    docker容器如何安装vim
    Maven+Docker,发布到Registry
    Maven + Docker
    Jenkins-SVN + Maven + Docker
  • 原文地址:https://www.cnblogs.com/guochaoxxl/p/16157754.html
Copyright © 2020-2023  润新知