• C语言标准宏获取文件名、行号、函数名的方法以及#和##的用法


    一、前言

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

    二、ANSI C标准宏

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

    三、实例

    宏实例:

    #include <stdio.h>
    
    int main()
    {
        printf("__func__:%s
    ", __func__);
        printf("__FILE__:%s
    ", __FILE__);
        printf("__DATE__:%s
    ", __DATE__);
        printf("__TIME__:%s
    ", __TIME__);
        printf("__LINE__:%d
    ", __LINE__);
    
        return 0;
    }

    宏实例程序输出如下:

    __func__:main
    __FILE__:main.c
    __DATE__:Sep 14 2019
    __TIME__:14:26:36
    __LINE__:9

    四、#和##运算符

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

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

    实例1:

    #define P(A) printf("%s:%d
    ", #A, A);
    int main()
    {
        int a = 1, b = 2;
        P(a);
        P(b);
        P(a+b);
        
        return 0;
    }

    实例1程序输出如下:

    a:1
    b:2
    a+b:3

    实例2:

    #define SQUARE(x) printf("The square of "#x" is %d.
    ", ((x)*(x)))
    int main()
    {
        SQUARE(8);
        
        return 0;
    }

    实例2程序输出如下:

    The square of 8 is 64

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

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

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

    实例程序:

    #include <stdio.h>
    
    #define XNAME(n) SYSTEM_ ## n
    
    int main()
    {
        int SYSTEM_ = 0,
            SYSTEM_OPEN = 1,
            SYSTEM_WRITE = 2,
            SYSTEM_CLOSE = 3;
        
        printf("%d
    ", XNAME());
        printf("%d
    ", XNAME(OPEN));
        printf("%d
    ", XNAME(WRITE));
        printf("%d
    ", XNAME(CLOSE));
    
        return 0;
    }

    实例程序输出:

    0
    1
    2
    3

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

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

    ##__VA_ARGS__的实例:

    #define my_print1(fmt,...) printf(fmt, __VA_ARGS__)
    my_print1("iiijjj
    ")        // 错误打印
    my_print1("i=%d,j=%d
    ",i,j) // 正确打印
    
    #define my_print2(fmt,...) printf(fmt, ##__VA_ARGS__)
    my_print2("iiijjj
    ")        // 正确打印
    my_print2("i=%d,j=%d
    ",i,j) // 正确打印
  • 相关阅读:
    关于网页编码转换的几种方法
    java加载静态properties的方法
    js 点击 li 获取 设定的参数信息
    添加网关信息
    SQL 函数
    SQL Date函数
    SQL 视图(VIEW)
    SQL高级语法3
    SQL 约束
    SQL 创建表
  • 原文地址:https://www.cnblogs.com/macrored/p/11519145.html
Copyright © 2020-2023  润新知