• C语言宏的几种常用语法与示例



    tags: C/C++

    写在前面

    简单总结一下C语言中x-macros(宏)的几种主要用法, 虽然主流的C语言程序中一般是不推荐使用宏的, 但是作为一种不错的C语言语法糖, 了解一下也不错,至少能看懂点大佬的代码. 主要参考了C语言程序设计:现代方法第十四章预处理器.

    基本语法

    直接展开

    其实就是简单的展开而已.

    #include 
    
    #define n1 100
    #define M (n1*n1+3*n1)
    
    int main() {
        int sum;
        sum = 3 * M + 4 * M + 5 * M;
        printf("sum=%d\n", sum);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这段程序中就是一个预处理器(宏)的简单用法, 直接进行简单的(无脑)替换操作.

    带参数的宏

    #include 
    #define printn(n) printf("n=%d\n", n)
    
    int main() {
        int sum = 20 + 12;
        printn(sum);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    井号的用途

    单个井号表示变量替换, 后面跟上要替换的变量; 两个井号挨着表示连接.

    #include 
    #include 
    void t1() {
        // #表示变量替换
    #define AREA(x,y) printf("长为"#x",宽为"#y"的长方形面积为%d.\n",(x)*(y));
    
        int i = 55, j = 3;
        AREA(i, j);
        AREA(3, 2);
        /*
        长为i,宽为j的长方形面积为165.
        长为3,宽为2的长方形面积为6.
        */
    }
    
    
    //##表示连接
    #define VAR(n)v##n
    
    int v1(int a, int b) {
        return a + b;
    }
    int v2(int a, int b) {
        return a - b;
    }
    void t2() {
        printf("%d\n", VAR(1)(2, 3));
        printf("%d\n", VAR(2)(2, 3));
        /*
        5
        -1
        */
    }
    
    int main(int argc, char const *argv[]) {
        // t1();
        t2();
        return 0;
    }
    
    
    
    • 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

    预定义的宏

    C语言中预定义了一些宏, 可以用于调试代码.

    名称描述
    __LINE__被编译的文件中的行号
    __FILE__被编译的文件名称
    __DATE__编译日期(mm dd yyyy)
    __TIME__编译时间((hh:mm:ss))
    __STDC__编译器符合C标准(89or99),值为1
    __func__存储当前正在执行的函数的名字的字符串变量

    这里直接用之前写好的C++代码了,C也一样,改成printf即可.

    #include 
    using namespace std;
    
    #define LOG(x) (cout<<__FILE__<<" "<<__LINE__<<": "<<__PRETTY_FUNCTION__<<(x)<<endl)
    
    
    void t1() {
        LOG("hello");
    }
    
    template<typename T>
    void t2(T s) {
        LOG(s);
        cout << __TIME__ << endl;
        cout << __DATE__ << endl;
        cout << __func__ << endl;
        /*
        gcc_pre_define.cc 13: void t2(T) [with T = char]1
        00:17:03
        Aug 24 2022
        t2
        */
    }
    
    int main(int argc, char const *argv[]) {
        // t1();
        t2('1');
        return 0;
    }
    
    • 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

    任意个数的参数的宏(C99)

    #include 
    #include 
    // 测试函数的用法是: 满足参数条件的话, 执行打印"通过测试", 否则执行自定语句
    #define TEST(cond, ...) \
    ((cond) ? printf("Passed test: %s\n", #cond) : \
    printf(__VA_ARGS__))
    
    void t1() {
        int v = 10, maxV = 20;
        TEST(v <= maxV, "v %d exceeds %d\n", v, maxV);
        v = 100, maxV = 20;
        TEST(v <= maxV, "v %d exceeds %d\n", v, maxV);
        /*
        Passed test: v <= maxV
        v 100 exceeds 20
        */
    }
    
    int main(int argc, char const *argv[]) {
        t1();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    …记号(省略号)出现在宏参数列表的最后,前面是普通参数。__VA_ARGS__是一个专用的标识符,只能出现在具有可变参数个数的宏的替换列表中,代表所有与省略号相对应的参数。

    (至少有一个与省略号相对应的参数,但该参数可以为空。)宏TEST至少要有两个参数,第一个参数匹配condition,剩下的参数匹配省略号。

    注意事项

    1. 在每一个宏替换的变量两边都要加爱上圆括号, 这样可以防止宏替换之后的代码意义改变.
    2. 多行的宏定义, 在断行位置需要加上反斜杠(转义);
    3. 单一的井号后接变量名, 这种用法放在printf中, 隐含了C语言字符串字面量可以直接合并.

    更多细节请看上面提到的书, 对一些比较特殊的例子也有解释.

  • 相关阅读:
    拓展卡尔曼滤波(Kalman)附Matlab代码
    C语言网络编程基础(linux)
    接口设计与优化
    物联网与 Linux 的相爱相生
    Mybatis实战练习六【批量删除&Mybatis参数传递】
    (02)Cartographer源码无死角解析-(13) 开始轨迹→Node::AddTrajectory()
    Vue动态切换class属性:数组法、对象法
    资源调度问题——贪心算法
    Kotlin 协程异常全局捕捉
    道德与社会问题简报 #3: Hugging Face 上的道德开放性
  • 原文地址:https://blog.csdn.net/qq_41437512/article/details/126672755