作者的码云地址:https://gitee.com/dongtiao-xiewei
后续作者会更新力扣的每日一题系列,原代码会全部上传码云,推荐关注哦,笔芯~
还像更深入地了解c语言?快来订阅作者的c语言进阶专栏!作者承诺本系列不会TJ!预计更新:指针,字符串处理,内存管理,结构体,预处理等等
感谢朋友们的支持,我们这个系列今天终于完结啦!
我们一直以来写的代码,
本质其实都是以.c文件为后缀名的文本文件
,例如我们随便以记事本形式点来一个.c后缀的文件
点开我们看到,就是我们熟悉的代码样式
但这就有一个问题,
这种文本文件是如何被计算机识别的呢?
其实计算机能够识别的就只有
机器语言
,也就是
一串二进制代码
,早期的程序员也是全用机器语言编程,后面为了避免麻烦,简化编程的过程,就产生了
较为容易理解汇编语言
,而汇编过程就是将汇编语言转化为机器语言
而为了解决平台的可移植性和通用性的问题,又产生了
高级语言
,例如c语言,basic语言等,而
编译
过程,可把
高级语言转化为汇编语言
所以,现代计算机可分为以下几个层次
我们的c代码若要生成一个
可执行程序
(二进制代码)需要进行以下几个步骤
今天的章节我们重点介绍预处理环节
预处理环节将会进行以下工作
- 处理define定义好的标识符,宏等
- 去掉注释
我们可以先测试一下去掉注释
int main()
{
printf("hello world!\n");//you can see me!
return 0;
}
预处理后的结果:
注:由于VS编译器查看预处理后的代码比较困难,所以本章节的代码我们使用linux平台测试
本节用到的linux指令:
touch test.c创建一个源文件
vim test.c 编写代码
gcc -E test.c -o test.i 将预处理后的代码保存在test.i中
好,坐稳扶好,我们正式开始喽!
#define
#define定义标识符
通用格式:
#define name stuff
例子:
#define MAX 200
#define str struct
我们用这个定义标识符可以起到定义常量,重命名,同名替换等等
可以在linux上简单测试一下
#define MAX 200
#define reg register
int main()
{
reg int a=MAX;
printf("%d\n",a);
return 0;
}
而代码也能跑的过去
我们可以看到,
预处理阶段就完成了简单替换
萌新最容易犯的错误:
在定义完成后加上’’;’’
#define MAX 200;
虽然这个代码可能在有些情况不会出错,但替换下来就是这个结果
看起来很奇怪,是吧!
#define定义宏
这是define的一个特殊机制,允许把
参数替换在文本中
例子
#define DOUBLE(x) (x+x)
通用格式
#define name(list) stuff
宏的使用
虽然是宏,但我们可以观察到,本质也是
简单替换
我们同样也可以定义以下的宏
#define SQUARE(x) x*x
但是注意,这个宏存在一个问题,假如我们要算这个表达式的结果
SQUARE(5+1)
我们期望结果当然是6的平方,也就是36
但是我们测试发现结果并不是36
结果居然是11!
我们可以打开test.i文件查看一下预处理后的代码
我们可以发现,
宏仅仅只是完成了简单替换,并没有考虑优先级的问题
这个其实就是根据替换后的算术优先级来计算结果
1*5=5+5+1=11
当然,
为了避免算术优先级的问题
,我们可以对代码做一点改进
#define SQUARE(x) ((x)*(x))
这样修改后,宏就会是相对独立的运算结果了
条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
有以下条件编译指令
#ifdef,#ifndef,#if等等
注意,条件编译的末尾一定要加上#endif!
#ifdef
只有你编译了某个宏,代码才会被执行
比如我们在要调试一个代码的场景
这里显示了打印字符,如果我们把它注释掉,就不会显示
ifndef
同样还是上面的代码,但是却与上面功能相反
没有定义,才执行下列的程序
应用场景:头文件防止被重复包含
#ifndef __TEST_H__
#define __TEST_H__
//代码
#endif
#if
与分支语句的if相同,例如