篮球哥温馨提示:编程的同时不要忘记锻炼哦!

人间骄阳刚好 风过林梢,彼时我们正当年少


目录

1、#define的深度认识

1.1 数值宏常量

1.2 字符串宏常量

1.3 用宏充当注释符号

1.4 用宏替换多条语句

1.5 宏定义的使用建议

2、#undef 撤销宏

2.1 宏的定义位置和有效范围

2.2 宏的取消

2.3 一道笔试题


1、#define的深度认识

1.1 数值宏常量

宏定义数值常量相信大家都不陌生,相信很多小伙伴用过,这里我们就简单的提一下,我们前面也讲过,#define 本质上是替换,它可以出现在代码的任何地方,也可以把任何东西都定义成宏,编译器会在预编译的时候进行替换掉,举例:

#dfeine PI 3.1415926

这样在以后的代码中你就可以用 PI 来代替 3.1415926 那么这样做的好处是什么呢?假设在未来的某一天,你要提升这个精度,如果你代码中出现 3.1415926 过多的话,你提升精度还得一个个修改, 如果使用宏定义的话,你只需要改一次即可。

1.2 字符串宏常量 

除了宏定义常量之外,还经常用来定义字符串,特别是路径:

① #define PATH_1 D:\code\lesson1\test

② #define PATH_1 "D:\code\lesson1\test"

以上哪个是正确的呢?如果觉得太长还可以用续行符:

③ #define PATH_1 "D:\code\lesson1\\

test"

很显然第一个肯定是不对的,字符串需要用 "" 引起来,第三个也不对,第二个呢?我们去实践证明下(以上写法都不推荐!):

在Linux平台环境下:

在Windows环境下: 

很显然他们都有同样的警告,都是未知转义序列,也无法正确打印出我们的路径,在前面我们讲到,' \ ' 是转义字符,当我们要打印路径的时候需要用转义字符 ' \ ' 去还原 ' \ ' 的字面意思,所以这里打印路径要用 \\ !

注意:Windows路径分隔是用 ' \ ',而Linux路径分隔是用 ' / ',所以如上测试用例改成 ' / ' 的话是不会报警告的。

所以要正确的打印如上用例应该这样写:

//不使用续行符
#define PATH_1 "D:\\code\\lesson1\\test"//使用续行符
#define PATH_1 "D:\\code\\lesson1\\\
test"

1.3 用宏充当注释符号

因为 Linux 环境能直接查看预处理过程,便于我们验证,所以我们下边会在 Linux 环境下测试。

我们先简单了解下程序的翻译过程:

  1. 预处理-E:头文件展开,去注释,宏替换,条件编译...
  2. 编译-S:将预处理后的C语言翻译成汇编语言
  3. 汇编-c:将汇编语言转化为可目标二进制文件( 可被链接 )
  4. 链接:将目标二进制文件与相关库链接,形成可执行程序

这里我们来看一段用宏充当注释符号的代码:

#include <stdio.h>
#define BSC //
int main()
{BSC printf("hello world\n");printf("you can see me!\n");return 0;
}

这里我们要探讨一个什么问题呢?如果替换成功,则不会执行第一个函数,如果替换失败,则我们会看到两行打印:

这究竟是为什么呢?我们可以执行:

[lwp@localhost code]$ gcc -E test.c -o test.i

把预处理后的结果保留下来为 test.i 文件,接着我们可以去用 vim 编辑器查看一下它与源文件的区别在哪,究竟是如何替换的:

通过上图我们可以发现,在预处理之后的文件中,并没有去成功通过宏替换注释掉第一个 printf 函数,由此可见,在预处理阶段,是先执行去掉注释,然后在进行宏替换,如上代码,本质是直接定义了一个空宏,我们特别不推荐这样写代码!(C语言注释风格也一样不行,感兴趣可以下去尝试下)

1.4 用宏替换多条语句

先看一段代码:

#include <stdio.h>
#define INIT_VALUE(a, b) a = 0; b = 0;
int main()
{int flag = 0;scanf("%d", &flag);int a = 100;int b = 200;if (flag)INIT_VALUE(a, b);elseprintf("%d, %d\n", a, b);return 0;
}

我想请问,这段代码有问题吗?应该如何改进呢?这段代码明显是编译不会通过的,但是可以通过执行预处理指令,发现预处理并没有出问题,那么,我们可以看一下预处理之后的结果与源文件的区别在哪:

通过预处理之后的结果我们可以看到,宏替换多了一个分号。于是有小伙伴就讨论出来如下三种解决方法:

  1. 去掉宏定义的最后一个分号
  2. 规范代码风格,给 if 和 else 加上大括号
  3. 给宏定义要替换的部分用大括号括起来

第一种解决方法肯定是不行的,去掉最后一个分号并不能解决问题,if else 在没有大括号的情况下后面只能跟一条语句,所以第一条行不通。

第二种解决方案看似不错,但是我们有没有想过,并不是所有人都会有良好的代码风格,我们作为程序员,写出的宏应该具有健壮性,所以第二条不可取。

第三种解决方案我们看着好像靠谱,但是我们通常写完一条语句中后面都会带上分号,那可想而知会出现这种情况:{a = 0, b = 0;}; 大括号外是不能跟分号的,所以这个方法也不可取!

最好的解决方法是什么呢?使用 do while 结构:

#include <stdio.h>
#define INIT_VALUE(a, b) do{a = 0; b = 0;}while(0)
int main()
{int flag = 0;scanf("%d", &flag);int a = 100;int b = 200;if (flag)INIT_VALUE(a, b);elseprintf("%d, %d\n", a, b);return 0;
}

循环会被看成一条复合语句,所以 if 不带大括号也没事(建议带上),这样我们的宏就会更健壮,也不会出错,同时你也可以在中间添加续行符,让他们的格式更清晰!同时我也有个小建议,宏定义的结尾最好都不要带分号。

结论: 当我们需要宏进行多条语句替换的时候,推荐使用 do-while-zero结构

1.5 宏定义的使用建议

【建议1】在宏定义体的结尾省略分号。

【建议2】函数宏的调用不能省略参数。

【建议3】函数宏的定义中,每个参数都应该以小括号括起来,避免替换之后出现优先级的问题。


2、#undef 撤销宏

2.1 宏的定义位置和有效范围

第一个问题,宏定义的位置有限制要求吗?

答案:源文件的任何地方,宏都可以定义,与是否在函数内外无关。

第二个问题,宏的有效范围有多大呢?

#include <stdio.h>
void test()
{printf("test: %d\n", M);}int main()
{test();#define M 10printf("main: %d\n", M);return 0;}

这段代码我们就发现编译不通过了,那么我们来进入预处理文件来对比下源文件:

答案:宏的作用范围,从定义处开始,往后都是有效的!

2.2 宏的取消

这里我们用一个例子就能很好的证明了:

 #include <stdio.h>                                                                                                                     2 3 #define M 104 int main()5 {6      printf("%d\n", M);7 #undef M8      printf("undef: %d\n", M);9      return 0;10 } 

我们来查看如上代码的预处理之后的结果:

结论:undef 是取消宏的意思,可以用来限定宏的有效范围! 

2.3 一道笔试题

#include <stdio.h>int main()
{
#define X 3
#define Y X*2
#undef X
#define X 2int z = Y;printf("%d\n", z);return 0;
}

请问小伙伴们,这段代码打印什么?

这里我就不截图给大家看了,感兴趣的可以自行下去敲一敲,经过Linux平台和windows平台的测试,最终打印的都是 4,因为编译器都是从上到下扫描代码的,以最近的宏定义为准。


To shine, not be illuminated.

【C语言】预处理的深入理解(第一期)相关推荐

  1. 国嵌c语言深度,国嵌C语言深度剖析班(第一期)-国嵌

    国嵌C语言深度剖析班(第一期)共20个课程,包含源码及教程,主要讲了基本数据类型分析.auto,register,static分析.if,switch,do,while,for分析等,本教程提供1-5 ...

  2. 【C语言】刷题计划第一期——洛谷编程题目集

    如果你已经掌握了一些C语言的基本语法,想练习自己写代码的能力,建议大家可以看一下博主整理题目,从入门到进阶都有.每个阶段需要的技能会有差异,大家要补充对应的基础知识.一起加油,那么一键三连我们开始发车 ...

  3. 天野第一期易语言模拟班

    第一期易语言模拟脚本培训班,主讲游戏--手游:时空猎人,端游:倩女幽魂(在线课程已结束,有全套视频教程及源码) 第一章 易语言基础 共12课时 1.易语言开发脚本常用组件和基本命令初步认识 2.跟我一 ...

  4. 第十四届蓝桥杯模拟赛(第一期)——C语言版

    1. 二进制位数 问题描述 十进制整数 2 在十进制中是 1 位数,在二进制中对应 10 ,是 2 位数. 十进制整数 22 在十进制中是 2 位数,在二进制中对应 10110 ,是 5 位数. 请问 ...

  5. 《跨语言文本相似性检测》第一周—前期调研

    <跨语言文本相似性检测>第一周-前期调研 文本相似度计算在信息检索.数据挖掘.机器翻译.文档复制检测等领域有着广泛的应用. 文本相似性流程 分词-->权重-->选择相应算法 文 ...

  6. 【蓝桥杯】第十四届模拟赛第一期及第二期填空汇总

    目录 1.A题(进制位数) 位运算符 第一期 问题描述 解析 第二期 解析 代码 2.B题(日期问题) 第一期 问题描述 解析 代码实现 执行结果 第二期 问题描述 解析 3.C题(数学问题) 第一期 ...

  7. 基于C99规范,最全C语言预处理知识总结

    基于C99规范,最全C语言预处理知识总结 00. 前言 1. 语法形式 2. 描述 3. 约束 4. 语义 01. 条件包含 01.1 关于`defined` 01.2 关于`#if/#elif/#e ...

  8. 【第一期】你留言,我送书

    一直以来,给读者回馈福利最好的方式就是送书."知识改变命运,书中自有黄金屋",任何时代都不过时的名言,没有之一!!昨天的公众号三周年送书活动:「三周年送书活动」还在持续中! 这次, ...

  9. 【eoe 特刊】Windows 8 第一期 -- C#基本语法---sqlite数据库的使用---ListView 滚动加载 ---等

    [eoe 特刊]Windows 8 第一期 本文档由 eoeWindowsPhone 开发者社区组织策划,整理及发布,版权所有,转载请保留 http://wp.eoe.cn/ 做最棒的 Windows ...

  10. c语言编译预处理指令大全,C语言预处理指令

    「今天是学习C语言第 32 天」 当你选择了一种语言,意味着你还选择了一组技术.一个社区.--Joshua Bloch # 预处理 C语言源程序先经过预处理器进行预处理,之后经过编译器编译成二进制可执 ...

最新文章

  1. 中国·北京创新创业大赛季(2020)参赛企业注册报名操作指南
  2. nginx配置静态资源html,通过nginx服务器访问静态资源(示例代码)
  3. Java中的字符串常量池详细介绍
  4. Java 面试题(3)—— JVM
  5. 记一次解决curl https证书问题
  6. java web 断点上传_使用WebUploader实现分片断点上传文件功能(二)
  7. 达成这个目标的数据分析师,在大厂HR眼里最新鲜吃香
  8. 给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数
  9. Linux核心进程管理命令
  10. C/C++ 16bit转8bit
  11. 安装compiz-fusion
  12. 国家自然科学基金重点项目启动暨软件定义网络 技术前瞻研讨会
  13. 【OpenPCDet】Kitti数据集下训练PointPillars并评估可视化
  14. 抖音小店还能做吗?如何解决拍单问题?
  15. Patch:虚拟DOM最核心的部分--如何对比虚拟DOM树,以及如果生成真实DOM
  16. 为什么cpu要一心二用:浅谈多线程编程的一个具体例子
  17. 俄罗斯一家庭与世隔绝40年 不知有二战(图)
  18. Python发送邮件报错554,求解答。
  19. 【数学几何】四等分的角度
  20. 计算机软考程序员客观题,软考程序员2000年到2018年真题试卷(有答案)

热门文章

  1. mysql 内部 临时表_MySQL内部临时表策略 - Mr.南柯 - 51Testing软件测试网 51Testing软件测试网-软件测试人的精神家园...
  2. access to同义替换_雅思写作高频100必备词(附高级替换)
  3. trick2-mobilenetv1、mobilenetv2、mobilenetv3替换YOLO主干
  4. 【pytorch】DCGAN实战教程(官方教程翻译)
  5. 什么是多尺度密集网络 - MSDNet ?
  6. execve系统调用_进程调度之 4:系统调用execve
  7. DS1302实时时钟
  8. 欧拉φ函数和欧拉降幂公式
  9. 魏德米勒端子eplan宏_Eplan部件库和宏全集
  10. Python验证信用卡号的有效性(算法)(称为Luhn检测或者mod 10 检测)