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\n", __func__);
    printf("__FILE__:%s\n", __FILE__);
    printf("__DATE__:%s\n", __DATE__);
    printf("__TIME__:%s\n", __TIME__);
    printf("__LINE__:%d\n", __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\n", #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.\n", ((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\n", XNAME());
    printf("%d\n", XNAME(OPEN));
    printf("%d\n", XNAME(WRITE));
    printf("%d\n", 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\n")        // 錯誤打印
my_print1("i=%d,j=%d\n",i,j) // 正確打印

#define my_print2(fmt,...) printf(fmt, ##__VA_ARGS__)
my_print2("iiijjj\n")        // 正確打印
my_print2("i=%d,j=%d\n",i,j) // 正確打印

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM