主页:114514的代码大冒险
qq:2188956112(欢迎小伙伴呀hi✿(。◕ᴗ◕。)✿ )
Gitee:庄嘉豪 (zhuang-jiahaoxxx) - Gitee.com
文章目录
目录
文章目录
前言
一、程序的翻译环境和执行环境
二、详解编译和链接
1.翻译环境
2.编译本身可分为几个阶段
3.运行环境
二,预处理详解
1.预定义符号
2.#define
2.1 #define 定义标识符
2.2#define 定义宏
2.3#define 替换规则
2.4 #和##
总结
前言
本文尽我可能地使用了足够通俗的语言
希望能够为你带来一些帮助
一、程序的翻译环境和执行环境
在ANSI C的任何一种实现中,存在两个不同的环境
·翻译环境,在这个环境中源代码被转换为可执行的机器指令
·执行环境,它用于实际执行代码
二、详解编译和链接
1.翻译环境
源文件经编译器处理,转化成后缀为obj(后缀名在不同系统会有所差异)的目标文件
每个源文件在这一过程中都是独立的
所有的源文件经过整合与链接库中的库函数通过链接器生成可执行程序
2.编译本身可分为几个阶段
计算机只能执行二进制指令
test.c-------->test.exe(可执行程序)
我们来看看这个过程具体发生了什么
环境由集成开发环境(IDE)VS2022提供
3.运行环境
有地址的对象只能是全局变量 和函数之类的
不能是临时变量
通过上图的地址的整合,于是形成了一张表格
这个表格就是符号表
瞎编的地址:
然后在链接阶段把无用的变量去掉
二,预处理详解
1.预定义符号
__FILE__ // 进行编译的源文件__LINE__ // 文件当前的行号__DATE__ // 文件被编译的日期__TIME__ // 文件被编译的时间__STDC__ // 如果编译器遵循 ANSI C ,其值为 1 ,否则未定义
__STDC__ IDE : vs2022未遵循ANSI C,所以无法正常打印
这些预定义符号都是语言内置的
2.#define
2.1 #define 定义标识符
举例:
- #define MAX 1000
- #define reg register //为 register这个关键字,创建一个简短的名字
- #define do_forever for(;;) //用更形象的符号来替换一种实现
- #define CASE break;case //在写case语句的时候自动把 break写上。
- // 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。#define DEBUG_PRINT printf("file:%s\tline:%d\t \
- date:%s\ttime:%s\n" ,\
- __FILE__,__LINE__ , \
- __DATE__,__TIME__ )
[提问] 那么define定义标识符的时候,要不要最后加上; ?
测试代码:
- #define MAX 1000;
- //#define MAX 1000
-
- int main()
- {
- int m = 0;
- if (m >= 0)
- m = MAX;
- else
- m = -1;
- printf("%d\n", m);
-
- return 0;
- }
说到定义标识符
我这里忽然想到了经常被用来使用在C语言Switch语句中的定义形式
#define CASE break;case
因为在别的语言中的,大多是不需要在每一个case结束时添加break;
所以为了避免出错
出现了上图所示的定义
定义前
- int main()
- {
- int n = 0;
- switch (n)
- {
- case 1:
- break;
- case 2:
- break;
- case 3:
- break;
- case 4:
- break;
- case 5:
- break;
-
- }
- }
定义之后:
- int main()
- {
- int n = 0;
- switch (n)
- {
- case 1:
- CASE 2 :
- CASE 3 :
- CASE 4 :
- }
- }
2.2#define 定义宏
- #define SQUARE(X) X*X
-
- //宏是替换
- int main()
- {
- printf("%d\n", SQUARE(5));
- printf("%lf\n", SQUARE(5.0));
- printf("%d\n", SQUARE(5+1));//?
-
- return 0;
- }
前两个printf打印结果很显然
那么对于最后一个呢?
它的结果是“36”吗?
emmmm,这值得考虑
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏( macro )或定义宏(define macro )。
很显然,宏是一种替换,那么
最后的那个printf对应的应该是 “5+1*5+1”
结果应当是“11”
怎么办?出现bug了!
答:加个括号控制一下优先级
#define SQUARE(X) (X)*(X)
嗯,这样就可以完全解决这个bug了
- #define DOUBLE(X) (X)+(X)
-
- int main()
- {
- printf("%d\n", DOUBLE(3));
- printf("%d\n",10*DOUBLE(3));
-
- return 0;
- }
那么这个宏定义是不是完美的呢?
显然不是啊,后一个printf对应的内容替换之后就成了“10*(3)+(3)”
这并不是我们想要的效果
于是,我们又重新考虑(依然是优先级的问题)
#define DOUBLE(X) ((X)+(X))
嗯,这个就解决了bug
所以在宏定义时,不要吝啬括号,大胆用就是
所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
2.3#define 替换规则
2.4 #和##
前提知识:
引出:
这里要写两个printf,太过于繁琐
所以我们希望可以实现一个宏,让它来完成这种效果
这样显然是不太可行的
于是就有了今天的主角“#”
作用:
把一个宏参数变成对应的字符串 。
于是乎:
#define PRINT(x) printf("the value of "#x" is %d\n",x)
比如说传参“a”,经过“#”的作用,就成了“printf("the value of""a"" is %d\n",x)”
也就是三个紧挨着的字符串,这个就可以完美呈现我们想要的效果
---------------------------------------------------------------------------------------------------------------------------------
- #define CAT(x,y) x##y
- int main()
- {
- int Class109 = 2023;
- printf("%d\n", CAT(Class, 109));
-
- return 0;
- }
由此可得“##”的作用
## 可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。
总结
那么,这就是本次的全部内容了,下半篇会在隔天之后补上,敬请期待
希望我的文章能够对你有所帮助