zoukankan      html  css  js  c++  java
  • C语言宏的使用

    使用条件宏进行条件编译

    譬如,对于同一份代码,我想编译出两个不同的版本,在其中一个版本中去掉某一部分功能,
    这时可以通过条件宏判断是否编译,例:
    C语言_宏1.png
    如果不使用条件宏进行控制,想编译两个不同版本的程序,就需要保存两份源代码。

    条件编译的语法和if else语法类似,必须以#endif结尾例如:

    #if 常量表达式
     //代码1
    #elif 常量表达式
     //代码2
    #elif 常量表达式 
     //代码3
    #else 
     //代码4
    #endif
    

    条件编译时将会根据常量表达式来求值,如果常量表达式的值为假,那么对应的代码将不会被 编译,还可以判断某个宏是否定义,例如:

    #ifdef(NameOfMacro)
         //代码1
    #else
         //代码2
    #endif
    

    如果NameOfMacro被定义,那么代码1将会参与编译,否则代码2参与编译,如下写法也是等价的:

    #if defined(NameOfMacro)
         //代码1
    #else
         //代码2
    #endif
    
    #if !defined(NameOfMacro)
         //代码2  
    #else
         //代码1
    #endif
    

    宏一般都定义在头文件中,要想使用改宏则使用#include包含该头文件即可,也可以定义在源文件中,但是这种情况比较少,一定要确保条件判断中的宏的定义位置位于条件判断之前,否则不会生效;宏的作用范围从定义它的位置开始到源文件结束,在其他源文件中该宏不可见,如果想使用某个宏,则必须使用#include指令包含对应的头文件。
    除了在头文件和源文件中定义宏,还可以在定义预处理宏,
    例如:在VS项目属性中
    C语言_宏2.png
    预处理宏只能是空的宏,但预处理宏作用于整个工程,对该工程中任意源文件可见,优先于任何定义在源码中的宏.

    使用空宏进行说明

    可以使用空宏对函数参数进行说明,微软的API中就使用了空宏,例如:

       WINBASEAPI
       BOOL
       WINAPI
       WriteFileEx(
                 _In_ HANDLE hFile,
                 _In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer,
                 _In_ DWORD nNumberOfBytesToWrite,
                 _Inout_ LPOVERLAPPED lpOverlapped,
                 _In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
                 );
    

    In,_Inout_定义如下:
    C语言_宏3.png
    _In_表示该参数为传入参数,Inout_表改参数为传入传出参数,为了防止_In,_Inout_被
    别人定了,所以先使用#ifdef判断该宏是否被定义,如果被定义则使用#undef取消定义,然后
    在重新定义该宏。

    #和##运算符

    除了#define 标识符 记号序列 这种形式定义宏,还可以定义带有参数的宏,形式如下:
    #define 标识符(标识符列表(可选)) 记号序列,
    在定义带有形参的宏定义中,#和##将会影响宏替换的过程,如果记号序列中某一个参数前
    面有一个#,那么替换时将会在这个参数前后插入",将其变成一个字符串,即使该参数是
    另一个带参宏,也不会在进行宏展开替换;如果该参数为一个字符串常量,那么预处理器会
    分别为字符串前后的"号进行转义,将"变成字符,然后在前后各插入一个";

    例:

     #define TEST0(p1) #p1"333"
     #define TEST(p1,p2,p3) #p1#p2 #p3
     int main()
     {
       int nTest1;
       int nTest2;
       int nTest3;
       printf("%s", TEST(TEST0(0),"333",nTest3));
       return 0;
     }
    

    运行结果:
    带参宏定义.png

    从上面的例子可以看出#运算符主要作用就是将宏定义中的参数变成字符串,而##运算符的
    作用就是将宏定义中相邻的两个的参数连接在一起组成一个标识符,如果该标识符不符合C
    语言表示符的组成规则,那么编译时会报错;
    例如:
    带参宏定义1.png

    对于使用了##和#的宏定义,预处理器需要进行反复扫描分析记号序列,以此来查找已定义的
    标识符进行替换,如果一个表示符在某次扫描中被替换后,再次扫描遇到此标识符时,则不在
    进行替换。
    例:

     #define TEST(p1,p2) p1##p2
     int main()
     {
       int nTest1;
       int nTest2;
       int nTest3;
       int TEST(TEST(nTest1, nTest2), nTest3) = 3;
       printf("%d", TEST(TEST(nTest1, nTest2), nTest3));
       return 0;
     }
    

    带参宏定义2.png

    使用/P命令查看编译预处理后的结果:
    大P命令.png

     int main()
     {
       int nTest1;
       int nTest2;
       int nTest3;
       int TEST(nTest1, nTest2)nTest3 = 3;
       printf("%d", TEST(nTest1, nTest2)nTest3);
       return 0;
     }
    

    可以看出TEST(TEST(nTest1, nTest2), nTest3)第一次展开后为TEST(nTest1, nTest2)nTest3,
    然后又遇到了TEST宏定义,则不在进行展开,直接将TEST(nTest1, nTest2)nTest3作为最终结果,但是
    TEST(nTest1, nTest2)nTest3显然不符合C语言表示符的定义,所以编译时会报错。

    注意:/P命令使用完成后,必须从命令行中删除它,否则编译的时候会报找不到xxxxx.obj错误

    #和##运算符是C语言宏定义中两个比较有创造力的运算符,可以创造出许多"黑魔法"宏定义

    预定义的宏

    C语言中有些预定义的宏,这些宏不能取消定义和重定义:
    C语言标准预定义的宏.png

  • 相关阅读:
    【手绘漫画】图解LeetCode之x 的平方根(LeetCode 69题)
    tcp 发送长度9 实际组包49
    tcp 发送报文长度和响应报文长度
    http 响应报文
    中台翻车纪实:一年叫停,员工转岗被裁,资源全浪费
    再也不怕女朋友问我二分查找了!【手绘漫画】图解二分查找(修订版)(LeetCode 704题)
    我的Hexo-Github博客搭建笔记
    J
    怎样使用npm打包公布nodejs程序包
    JQuery Jcrop—JQuery Jcrop 图像裁剪工具学习
  • 原文地址:https://www.cnblogs.com/UnknowCodeMaker/p/11101564.html
Copyright © 2011-2022 走看看