• 【程序的编译和预处理】


     

    1.程序的翻译环境&2.执行环境

    C语言程序实现的两种环境:

    第一步:翻译环境--使得源程序转换为机器可执行的机器指令

    第二步:执行环境--实现可执行代码

    3.详解:程序的编译和链接(翻译环境)

    多个test.c文件,多个test.obj,生成一个test.exe

    编译器介绍:

     链接库:库文件里的库函数/第三方库

    4.预处理符号详解

    4-1内置的预处理符号

    1. int main()
    2. {
    3. for (int i = 0; i < 10; i++)
    4. {
    5. printf("name:%s\tfile:%s \tline:%d \tdate:%s \ttime:%s \ti:%d\n",__func__,__FILE__, __LINE__, __DATE__, __TIME__);
    6. }
    7. return 0;
    8. }

     5.预处理指令

    5-1#define定义符号

    1. #define NUM 100
    2. #define STR "hello world"//字符串也可以使用预处理定义符号

    5-2#define定义宏

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

    注意:

    1. #define定义符号和宏的时候不要带分号
    2. 参数列表的左括号必须和name紧邻(函数可以,宏不可以)
    3. 写宏的时候,对于参数不要吝啬括号
    1. #define NUM 100;//错误用例1
    2. #define DOUBLE (x) x*x//错误用例2和3

    5-3#define替换规则

    1. #define M 100
    2. #define DOUBLE(x) ((x)+(x))
    3. int main()
    4. {
    5. int a = DOUBLE(M);
    6. printf("%d\n", a);
    7. return 0;
    8. }
    9. //第一步:-替换M- int a=DOUBLE(100)
    10. //第二步:-替换X- #define DOUBLE(100) 200
    11. //第三步:-替换DOUBLE(100)- int a=200;

     6。#和##宏的妙用

    6-1#

    6-1-1例子1:单纯只是研究辅助打印的信息,没有考虑参数的类型

    问:怎么把参数插入到一个字符串中?

    1. 想法2:函数
    2. //void Print(int n)
    3. //{
    4. // printf("the value of n is &d\n", n);
    5. //}
    6. //想法3:宏
    7. //#define PRINT(N) printf("the value of N is %d\n",N)//想法3
    8. //int main()
    9. //{
    10. // int a = 10;
    11. // //printf("the value of a if %d\n", a);
    12. // Print(a);
    13. //
    14. // int b = 20;
    15. // //:想法1:一个一个打
    16. // //printf("the value of b is %d\n", b);
    17. // PRINT(b);
    18. //
    19. //
    20. // return 0;
    21. //}
    22. //想法4:(最满足用户的做法)#
    23. #define PRINT(N) printf("the value of "#N" is %d\n",N)
    24. int main()
    25. {
    26. //基石
    27. printf("hello world\n");
    28. printf("hello ""world\n");
    29. int a = 10;
    30. PRINT(a);
    31. //等价于:printf("the value of ""a"" is %d\n",N);
    32. return 0;
    33. }

     

    6-1-2:考虑到传入的参数的类型 (这使得我想到函数重载)

    1. #define PRINT(N) printf("the value of "#N" is %d\n",N)
    2. int main()
    3. {
    4. int a = 10;
    5. double pai = 3.14;
    6. PRINT(a);
    7. PRINT(pai);
    8. return 0;
    9. }

     6-2##

    作用:##可以把位于它两边的符号合成一个符号

    它允许宏定义从分离的文本片段创建标识符

    1. #define CAT(name,num) name##num
    2. int main()
    3. {
    4. int song100 = 105;
    5. printf("%d\n", CAT(song, 100));
    6. //等价于printf("%d\n",song100)
    7. return 0;
    8. }

     这里我想解释一下一个东西:

    解释:先进行预处理(先合成了classi),再编译

    6-3带有副作用的宏参数

    ++在宏中的副作用

    1. #define MAX(m,n) ((m)>(n)?(m):(n))
    2. int main()
    3. {
    4. //int a = 0;
    5. //int b = a + 1;
    6. //b = a++;//带有副作用的语句
    7. //带有副作用的宏参数
    8. int a = 10;
    9. int b = 20;
    10. int c = MAX(a++, b++);
    11. //相当于int c=(a++)>(b++)?(a++):(b++);
    12. // 11 21 22
    13. printf("%d\n", a);//11
    14. printf("%d\n", b);//22
    15. printf("%d\n", c);//21
    16. return 0;
    17. }

    原因:

    • 宏的参数是不带计算的替换的(函数的参数是带计算拷贝的)
    • 如果宏中有多份++就会执行多次

    7.宏和函数的对比(蓝色标明考虑角度)

                                                             宏和函数的对比                                                     

    宏的优点:

    1. 没有函数调用和函数返回的开销
    2. 宏的参数与类型无关

    宏的缺点:

    1. 宏是没有办法调试
    2. 宏在使用不当,可能会带来运算符优先级和++的副作用问题
    3. 宏是没办法递归

    8.条件编译

    应用:stdio.h头文件中好多这种东西,你要看得懂

    1. #define NUM 1
    2. int main()
    3. {
    4. //#if-#else-#endif 分支的条件编译
    5. #if 0
    6. printf("hehe\n");
    7. #else
    8. printf("haha\n");
    9. #endif
    10. //#if-#elif-(#else)-#endif 多分支的条件编译
    11. #if NUM==1
    12. printf("1\n");
    13. #elif NUM==2
    14. printf("2\n");
    15. #else
    16. printf("0\n");
    17. #endif
    18. //判断是否#define符号的两种方法
    19. //方法1:
    20. #if defined(NUM)
    21. printf("1\n");
    22. #endif
    23. //方法2:
    24. #ifdef NUM
    25. printf("2\n");
    26. #endif
    27. //判断是否#undefine符号的两种方法
    28. //方法1:
    29. #if !defined(NUM)
    30. printf("1\n");
    31. #endif
    32. //方法2:
    33. #ifndef NUM
    34. printf("2\n");
    35. #endif
    36. return 0;

    9.预处理指令#include

    9-1#include<stdio.h>和#inlcude"stdio.h"的区别

    查找策略:

    #include“include”:先在根目录的的文件中查找,没找到再去目标库里查找

    #include<stdio.h>:直接去目标库里查找

    所以你的#include<stdio.h>可以写成#include"stdio.h"

    但是你的contact.c中不能把#include"conta

    ct.h"写成#include<contact.h>

     9-2防止头文件被重复包含的两种方法:(写在头文件里的)

    多次包含了头文件的危害:平添了几千行代码,使得编译器处理起来压力大

    方法1:

    1. //test.c
    2. #include<stdio.h>
    3. #include"stdio.h"
    4. #include<stdio.h>
    5. //test.h
    6. #ifndef __TEST_H__
    7. #define __TEST_H__
    8. #endif

    方法2: 

    1. //test.c
    2. #include<stsdio.h>
    3. #include<stdio.h>//无效,这一次头文件并没有被包含
    4. #test.h
    5. #pragma once

  • 相关阅读:
    Badboy录制jmeter性能测试脚本
    MySQL高级-MVCC(超详细整理)
    儿童学生护眼台灯哪个牌子好?双十二儿童护眼台灯精选
    数字化转型的第一步、数据应用的最后一公里应该如何做?
    如何用JAVA写一个Windows服务
    「Goravel 上新」用户授权模块,让你简单的对非法用户 Say No!
    数据库索引的基本操作(sql语句)
    使用bat脚本以json格式导出mongo数据库中指定表数据
    Demo29单词规律
    01 【版本控制和Git的安装介绍】
  • 原文地址:https://blog.csdn.net/qq_64428099/article/details/125289059