宏界说与特别运用

打印 上一主题 下一主题

主题 851|帖子 851|积分 2553

目录

宏界说


数值宏常量

#define 宏界说是个演技非常高超的替身演员,但也会经常耍大牌的,所以我们用它要慎之又慎。它可以出现在代码的任何地方,从本行宏界说开始,以后的代码就都熟悉这个宏了;也可以把任何东西界说成宏。因为编译器会在预编译的时候用真身替换替身,而在我们的代码里面却又用经常用替身来帮忙。看例子:
​        #define PI 3.141592654
在此后的代码中尽可以使用 PI 来代替 3.141592654,而且你最好就这么做。不然的话,如果我要把PI的精度再提高一些,你是否愿意一个一个的去修改这串数呢?你能包管不漏不出错?而使用 PI 的话,我们却只必要修改一次。这种环境还不是最要命的,我们再看一个例子:
​        #define ERROR_POWEROFF -1
如果你在代码里不消 ERROR_POWEROFF 这个宏而将-1硬编码进代码里,尤其在函数返回错误代码的时候(每每一个开发一个体系必要界说很多错误代码)。肯怕上帝都无法知道-1 表现的是什么意思吧。这个-1,我们一般称为“魔鬼数”,上帝遇到它也会发狂的。所以,我奉劝代码里肯定不要出现“魔鬼数”。
关键字篇我们讨论了 const 这个关键字,我们知道const 修饰的数据是有类型的,而 define 宏界说的数据没有类型。为了安全,我建议以后在界说一些宏常数的时候用 const 代替,编译器会给 const 修饰的只读变量做类型校验,减少错误的可能。但肯定要留意const修饰的不是常量而是readonly的变量,const 修饰的只读变量不能用来作为界说数组的维数,也不能放在 case 关键字反面。

字符串宏常量

#define ENG_PATH_4 "E:\English\listen_to_this\listen_to_this_3"

用define宏界说注释符号?

可否使用宏界说的注释来注释代码?
  1. #define COMMENT //
  2. int main()
  3. {
  4.         COMMENT puts("hello");
  5. }
复制代码
第一眼看这个代码可能会搞不清步调是否实行打印,这个问题的解决我们必要知道预处理过程各步调的实行次序,
先看看步调预处理过程做了什么.

步调的编译过程

预处理: 预处理指令,头文件展开,去掉注释,宏替换,条件编译 (次序是怎样的?)
编译: C语言翻译成汇编语言
汇编: 将汇编代码转化成可重定向目标文件(可被链接)
链接: 自身步调+库文件进行关联,形成可实行步调

预处理中宏替换和去注释谁先谁后?

生成的预处理效果如图:

观察效果,如果宏替换先于去注释,则puts代码肯定是被去掉的,显然puts还在,说明先去注释,再宏替换;
既然是先去注释再宏替换,那为什么预处理后却没有发现puts前面带双斜杠呢? 这就很尴尬了,其着实#define COMMENT //处的双斜杠在编译前就被识别成注释了,去掉注释后代码就变成了#define COMMET这样子,是一个仅仅用于标识的宏.
预处理指令和宏谁先处理是不可预期的.
总之,通过这点我们知道了预处理过程去注释是先于宏替换的.
上面说的是C++风格的注释,那C风格的注释呢
  1. #define BSC //
  2. #define BMC /*
  3. #define EMC */
  4. BSC: Begin Single-line Comment
  5. BMC: Begin Multi-line Comment
  6. EMC: End   Multi-line Comment
复制代码

这就很显着了,如果有语法提示则很轻易看出来,和上面所说的C++风格注释的环境是一样的原理.



如何写一个可靠的宏函数

我们知道,一般的宏函数是很轻易出现问题的,比如说少加了括号,因为联合性问题导致代码逻辑没有按照预期来实行...,那怎样写出一个结实性很高的宏函数呢? 先看一个例子:

如果我界说这样一个宏函数,而且按照一般函数的方式运用,显然不是能通过语法检查的.看一下预处理后的代码

可以发现a = 0;已经算一条语句了,反面b = 0;;多出来,不符合语法,因此报错.


if在不带花括号的条件下只能且必须带一条语句.如果想用这条宏函数,只能将它写进if的花括号中.但是,这样的代码是不友爱的,它变相的强迫用户必须带上花括号,显然不是一种很好的方式.
既然要求if分支有多条语句必要实行时必须加上花括号,那能不能直接在宏函数中加上花括号? 看一下效果

再看一下预处理后的代码

可以发现if花括号反面还带上了分号,这显然也不够好.
上面各种方式都是有大大小小的缺陷. 那还有没有更好的方案? 有的,最终解决方案:使用do-while-zero布局

do-while-zero布局


看预处理后的代码:

可以发现,在do-while-zero布局中,do反面有花括号,可以封装恣意多条语句.while(0)后可以接上分号,而且while(0)是条件判断为假,结束实行循环,整体上只实行一次且必须实行一次.用法上和普通函数有类似的效果,因此具有普适性.

do-while-zero的评价

do-while-zero布局是一个编码技巧,作为一个宏函数技巧,我们可以了解一下,虽然不愿定会使用它.在早些年的项目中也有很多使用的,掌握它后至少我们在看源码时可以在遇到这样子的宏函数时可以知道写的是什么...

宏界说中的空格
  1. #include <stdio.h>
  2. #define INC(a) ((a)++) //定义宏函数不能带空格
  3. int main()
  4. {
  5.    int i = 0;
  6.    INC (i); //使用可以带空格,但是严重不推荐
  7.    printf("%d\n", i);
  8. }
复制代码
宏只能在main函数上面界说吗?

先说结论:宏可以在源文件的任何地方界说.
验证,在main函数中界说:

在普通函数中界说:

在普通函数中界说,在main函数中使用:

说明:宏界说与是否在函数体外内没有任何关系
结论:源文件的任何地方,宏都可以界说,与是否在函数内外无关.

宏的作用范围

留意:宏只在从它界说的位置开始生效.从界说开始,往后都是有用的.
不正确例子:


#undef

#undef的作用是取消宏,限定宏的范围.

宏替换是在函数调用之进步行.

看下面一段代码:

#undef在函数调用的上边,这样的代码看着会有些绕.看一下运行效果:

这是可以通过的,因为宏替换是在函数调用之进步行.这样的代码必要熟悉预编译指令的实行次序才轻易阅读.

块中进行#define和#undef必要审慎

C语言中,尽管在代码文件中的任何位置放置#define大概#undef是合法的,但把它们放在块中会使人误解为它们只存在于块作用域,给人一种只在函数内有用的错觉.
也不清除我们只想让它在局部范围内有用,因此使用时必要慎重考虑.


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

八卦阵

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表