• #define定义标识符详解


    0.预定义符号

    在讲解#define之前先给大家介绍几个预定义符号

    1. __FILE__      //进行编译的源文件
    2. __LINE__     //文件当前的行号
    3. __DATE__    //文件被编译的日期
    4. __TIME__    //文件被编译的时间
    5. __STDC__    //如果编译器遵循ANSI C(标准C),其值为1,否则未定义

    这些预定义符号都是编译器自带的,可以直接使用,我们可以用printf函数去打印。

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

    1. 语法分析

                                    #define     name     stuff

    name代表的是stuff新的名字

    stuff代表的是实现的内容

    name的名字最好要全大写!!!!!

    就比如你总会用到一个数字,但是你还需要将它换值,如果我们不用#define来,我们就需要每次都写这个值,改值的时候也要一次次去修改,这给我们带来了很多的麻烦,所以这个时候我们可以使用#define来定义这个数值,给他取个名字,代表这个数值,改值的时候就可以直接在#define这里修改,改一次就行,大大提高了写代码的效率。

    记住这一点,#define可以将name替换成stuff。一定是替换,将name换成stuff

    比如:

    1. 代码1
    2. #include "stdio.h"
    3. int main()
    4. {
    5. int a = 10;
    6. int b = 20;
    7. int c = 30;
    8. if(a < 25)
    9. printf("aYES\n");
    10. if(b < 25)
    11. printf("bYES\n");
    12. if(c < 25)
    13. printf("cYES\n");
    14. return 0;
    15. }
    16. 代码2
    17. #define n 25
    18. int main()
    19. {
    20. int a = 10;
    21. int b = 20;
    22. int c = 30;
    23. if(a < n)
    24. printf("aYES\n");
    25. if(b < n)
    26. printf("bYES\n");
    27. if(c < n)
    28. printf("cYES\n");
    29. return 0;
    30. }

    代码1如果修改25需要修改3次,但是代码2通过#define定义后,只需要修改n后面的值就行,这就大大提高了修改的效率。

    1.1 举例分析

    1.1.1 代码1

    1. #define MAX 100
    2. int main()
    3. {
    4. int a = MAX;
    5. int a = 100;
    6. return 0;
    7. }

    我们来看第一个,我们用#define定义了MAX的数值是100,所以在主函数使用的时候

    int a = MAX;就相当于 int a = 100; 也就是把MAX替换成了100

    1.1.2 代码2

    1. #include "stdio.h"
    2. #define sz sizeof
    3. int main()
    4. {
    5. int a = sz(int);
    6. int b = sizeof (int);
    7. printf("%d %d",a,b);
    8. return 0;
    9. }

    第二个代码,是用#define来定义一个sz,代表sizeof关键字,所以在使用过程中,sz(int)就可以替换成sizeof(int)

    1.1.3 代码3

    1. 代码1
    2. #include "stdio.h"
    3. #define do_forever for(;;)
    4. int main()
    5. {
    6. do_forever
    7. {
    8. printf("1");
    9. }
    10. return 0;
    11. }
    12. 代码2
    13. #include "stdio.h"
    14. int main()
    15. {
    16. for(;;)
    17. {
    18. printf("1");
    19. }
    20. return 0;
    21. }

    这两个代码是等效的,代码1我们用#define定义了一个死循环,当编译的时候do_forever就会被替换成for(;;)

    所以#define可以定义任何东西,但是在使用时候会被自动替换成定义后面的东西

    1.1.4代码4

    1. 代码1
    2. #include "stdio.h"
    3. #define PRINT printf("file:%s\nline:%d\ndate:%s\ntime:%s\n",__FILE__,__LINE__,__DATE__,__TIME__)
    4. int main()
    5. {
    6. PRINT;
    7. return 0;
    8. }
    9. 代码2
    10. #include "stdio.h"
    11. #define PRINT printf("file:%s\n\
    12. line:%d\n\
    13. date:%s\n\
    14. time:%s\n",__FILE__,__LINE__,__DATE__,__TIME__)
    15. int main()
    16. {
    17. PRINT;
    18. return 0;
    19. }
    20. 代码3
    21. #include "stdio.h"
    22. int main()
    23. {
    24. printf("file:%s\nline:%d\ndate:%s\ntime:%s\n",__FILE__,__LINE__,__DATE__,__TIME__);
    25. return 0;
    26. }

    首先看代码1,我们是#define定义了一个PRINT,它是后面的一大串打印内容的,大家是不是也会觉得很长。

    所以我们优化看一下代码2,我们将其回车分成了4行,但是这里要记住,一定要在我们敲回车的地方➕一个\ ,告诉编译器这还是一个整体的内容,也就是将我们敲的回车转义了。如果不加这个\ ,整体代码就会出问题。

    1.2 #define末尾是否应该添加' ; '

    答案是最好不要加分号,那又是为什么呢?

    我们知道#define替换,比如上面我们是将MAX直接替换成了100sz直接替换成了sizeofdo_forever直接替换成了for(;;)等等。

    那我们如果加上了分号' ; '之后呢?

    1. 代码1
    2. #define MAX 100;
    3. int main()
    4. {
    5. int a = MAX;
    6. return 0;
    7. }
    8. 代码2
    9. #define MAX 100;
    10. int main()
    11. {
    12. int a = 100;;
    13. return 0;
    14. }
    15. 代码3
    16. #define MAX 100;
    17. int main()
    18. {
    19. int a = MAX
    20. return 0;
    21. }

    首先代码1代码2是等效的,我们可以看到加了分号之后,MAX是被替换成了100;,这里就有2个分号了,肯定是错误的。

    那怎么去修改呢?

    我们可以像代码3一样,在主函数那里也就是语句结尾,不加分号,等替换之后就有分号了,但是这跟咱们的书写习惯大大不同,所以最好不要加分号!

    2. #define定义宏

    2.1 语法分析

             #define nameparament-list ) stuff

    1.#define定义宏和函数很相似
    2.name相当于函数名

    3.parament-list 就相当于函数参数
    4.stuff就是相当于这个函数的功能

    注意⚠️⚠️:这里的左括号一定要跟name紧挨着
                           否则会name就会被替换成parament-list ) stuff

    比如:

    1. 代码1
    2. #define MAX(x,y) (x) > (y) ? (x) : (y)
    3. int main()
    4. {
    5. MAX(2,3);
    6. MAX(2,3);相当于:(2) > (3) ? (2) : (3);
    7. }
    8. 代码2
    9. #define MAX (x,y) (x) > (y) ? (x) : (y)
    10. int main()
    11. {
    12. MAX(2,3);
    13. MAX相当于:(x,y) (x) > (y) ? (x) : (y)
    14. }

    代码1左括号紧挨着name的,那我们就可以用MAX(x,y)完成我们想要实现的功能。

    代码2没有紧挨着的,这时候会出现错误,因为此时是MAX相当于(x,y) (x) > (y) ? (x) : (y)
    这时候MAX啥也不是,是个错误。

    所以左括号必须跟name紧挨着

    总而言之还是要记住#define是替换的作用。

    2.2代码举例

    2.2.1 代码1

    #define Square(x) x * x

    我们定义了一个Square(x),它的作用是求一个数的平方,参数是x

    Square(5);

    所以我们在引用的时候,给括号里数字5

    5 * 5

    就相当于5*5,可以求出来这个数的平方。

    但是如果是这样的代码,结果又会是多少呢?

    1. #include "stdio.h"
    2. #define Square(x) x * x
    3. int main()
    4. {
    5. int a = 5;
    6. printf("%d\n" ,Square( a + 1) );
    7. return 0;
    8. }

    正确答案是11
    我相信很多人都会得出36,但是我们一直在说#define是替换,所以上面代码又可以变成如下

    1. #include "stdio.h"
    2. #define Square(x) x * x
    3. int main()
    4. {
    5. int a = 5;
    6. printf("%d\n" ,a + 1 * a + 1 );
    7. return 0;
    8. }

    对吧,这时候Square(a+1)会被替换成 a+1*a+1,优先乘法运算,结果是11

    也就是说我们#define定义的,真的就是替换,你给什么替换什么,不会加括号,所以为了算出正确结果,一定要加括号,在#define宏定义时候,要多加括号

    正确代码:

    1. #include "stdio.h"
    2. #define Square(x) (x) * (x)
    3. int main()
    4. {
    5. int a = 5;
    6. printf("%d\n" ,Square( a + 1) );
    7. return 0;
    8. }

    2.2.2 代码2

    #define DOUBLE(x) (x) + (x)

    我们看这个#define定义的DOUBLE(x) 是求一个数的二倍,在这里我们给x加了括号避免了之前的错误,那下面代码输出的是什么呢?

    1. int a = 5;
    2. printf("%d\n" ,10 * DOUBLE(a));

    我相信大多数人现在肯定是算出正确结果了,因为之前吃的亏
    答案是55

    和我们预想结果不一样呀,我们预想的是100,为什么不一样呢?

    1. int a = 5;
    2. printf("%d\n" ,10 * 5 + 5);

    实际上,DOUBLE(a)是被替换成了5 + 5,但是前面有个10,还是乘法,就变成了10*5+5.那如何解决这个问题呢?还是加括号,代码如下

    #define DOUBLE(x) ((x) + (x))

    我们只需要整体加个括号就可以完美解决问题啦!

    总结:

    在#define定义宏的时候也就是类似函数,一定要多加括号,避免错误

    3.#define的替换规则

    3.1 规则1

      规则1 : 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号

                   如果,它们首先被替换

    这是什么意思呢?我们拿代码举例:

    1. #include "stdio.h"
    2. #define n 100
    3. #define MAX(x,y) ((x) > (y) ? (x) : (y))
    4. int main()
    5. {
    6. int a = 20;
    7. int c = MAX(a,n);
    8. printf("max是%d\n",c);
    9. return 0;
    10. }

    MAX(x,y)里,主函数传的参数里分别是a,n

    a是我们定义的局部变量,而n是#define定义的符号,所以根据规则,要首先被替换。

    就变成了这样:

    int c = MAX(a,100);

    n被替换成了100,之后再去替换#define定义的宏

    int c = ((a) > (100) ? (a) : (100));

    3.2 规则2

    规则2:

    再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上

    述处理过程。

    这一点没什么说的,反正就是你在#define定义的时候一定要看先发生的是谁就行。

    4.‘#’和‘##’的作用

    4.1‘#’作用

    我们先看这样一段代码

    1. char* p = "hello ""bit\n";
    2. printf("hello"" bit\n");
    3. printf("%s", p);

    此时输出的是

    我们可以看到两个输出的都是一样的,所以我们发现字符串它是有自动连接的特点,也就是说我打印完“hello ”这个字符串,但是发现后面还有,那就接着打印。

    我们明确了这个之后,思考这样一个问题 ,如何把参数插入到字符串中?

    看这样一段代码:

    1. #include "stdio.h"
    2. #define PRINT(n) printf("n的值是%d\n", n)
    3. int main()
    4. {
    5. int a = 20;
    6. PRINT(a);
    7. return 0;
    8. }

    我们用PRINT想打印传入的参数的值,但是这么打印得到的结果是

    首先第一点我们是要求a的值,我希望打印出来可以告诉我a的值是20,那这个时候‘#’就派上用场了。

    1. #include "stdio.h"
    2. #define PRINT(n) printf(#n"的值是%d\n", n)
    3. int main()
    4. {
    5. int a = 20;
    6. PRINT(a);
    7. return 0;
    8. }

    我们首先要知道printf函数是可以打印字符串的,字符串有自动连接的特点
    我们想把a打印到屏幕上,就需要在n的前面加上‘#’这个符号,#n会被替换成"a"

    1. #define PRINT(n) printf(#n"的值是%d\n", n)
    2. PRINT(a);
    3. 替换成
    4. printf("a""的值是%d\n", a);

    这样我们就打印出来想要的结果了

    我们解决了名字的问题,但是如果输入个浮点数呢?别的类型呢?又该怎么办呢?

    1. #include "stdio.h"
    2. #define PRINT(n) printf(#n"的值是%d\n", n)
    3. int main()
    4. {
    5. int a = 20;
    6. PRINT(a);
    7. float b = 3.14;
    8. PRINT(b);
    9. return 0;
    10. }

    在这段代码里肯定是输出不来浮点数b的,因为%d被写死了,我们是否可以把打印的格式传参呢?

    1. #include "stdio.h"
    2. #define PRINT(n,format) printf(#n"的值是"format"\n", n)
    3. int main()
    4. {
    5. int a = 20;
    6. PRINT(a,"%d");
    7. float b = 3.14;
    8. PRINT(b,"%f");
    9. return 0;
    10. }

    我们是传入的字符串,所以在printf里可以直接写format,因为会被替换成

    1. #define PRINT(n,format) printf(#n"的值是"format"\n", n)
    2. PRINT(a,"%d");
    3. 替换
    4. printf("a""的值是""%d""\n", n);
    5. PRINT(b,"%f");
    6. 替换
    7. printf("b""的值是""%f""\n", n);

    这样就可以打印出来任何数据类型了。

    4.2 ##的作用

     ##可以把位于它两边的符号合成一个符号,这个用途不大,但是很新奇。

    比如:

    1. #define Cat(x,y) x##y
    2. int main()
    3. {
    4. int helloworld = 100;
    5. printf("%d\n", Cat(hel,loworld));
    6. return 0;
    7. }

    大家可以想一想输出的结果是多少?

    我们一步一步来,先替换

    1. Cat(hel,loworld)
    2. 替换成
    3. hel##loworld

    而##还可以连接左右的符号

    1. hel##loworld
    2. 变成
    3. helloworld

    我们这个程序输出的就是helloworld的值,100.

  • 相关阅读:
    从0开始python学习-54.python中flask创建MD5和base64加密校验的接口
    24、CSS基础——定位以及透明通道
    突破限制, 访问其它 Go package 中的私有函数
    【C++风云录】梦幻般的机器人世界:探索ROS、PCL、OpenCV和更多顶尖技术
    攻防世界题目练习——Web引导模式(三)(持续更新)
    RenderThread 崩溃问题调查
    微信小程序-语音输入(录音并播放)
    python+flask计算机毕业设计基于微信小程序的首饰商城系统(程序+开题+论文)
    [附源码]SSM计算机毕业设计拾穗在线培训考试系统JAVA
    使用香橙派学习 Linux的守护进程
  • 原文地址:https://blog.csdn.net/2302_76941579/article/details/133066261