2021-10-16 C语言的宏

2021-10-16 C语言的宏,第1张

2021-10-16 C语言的宏 4个例子




宏前言

define是C语言中的预处理命令,它用于宏定义

预处理命令以“#”号开头,如包含命令#include,宏定义命令#define等

预处理(或预编译):指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。
预处理指令指示在程序正式编译前就由编译器进行的 *** 作,可放在程序中任何位置。

预处理是C语言的一个重要功能,它由预处理程序负责完成。
当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译

C语言提供多种预处理功能,主要处理#开始的预编译指令,如宏定义(#define)、文件包含(#include)、条件编译(#ifdef)等
合理使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计

宏定义

C语言源程序中允许用一个标识符来表示一个字符串,称为“宏” 被定义为宏的标识符称为“宏名”
在编译预处理时,对程序中所有出现的宏名,都用宏定义中的字符串去代换,这称为宏替换或宏展开

宏定义是由源程序中的宏定义命令完成的
宏替换是由预处理程序自动完成的

宏定义分为有参数和无参数两种

不带参数的宏

形式

“#”表示这是一条预处理命令(以#开头的均为预处理命令)
“define”为宏定义命令
“标识符”为符号常量,即宏名
“字符串”可以是常数、表达式、格式串等

宏定义用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名 只是一种简单的文本替换,预处理程序对它不作任何检查
如有错误,只能在编译已被宏展开后的源程序时发现

【例1】

#define MAX_TIME 100//不需要;因为在预处理时执行

在程序里面写if(time < MAX_TIME){…},则编译器在处理该代码前会将MAX_TIME替换为1000
这种情况下使用const定义常量可能更好
如const int MAX_TIME = 1000
因为const常量有数据类型,而宏常量没有数据类型
编译器可以对前者进行类型安全检查,而对后者只进行简单的字符文本替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误

【例2】

#define pint (int*)//不需要;因为在预处理时执行
pint pa, pb;

本意是定义pa和pb均为int型指针
但实际上变成int* pa,pb
pa是int型指针,而pb是int型变量

本例中可用typedef来代替define

typedef pint int*;//需要;因为宏是在预处理时执行,而typedef是在编译时执行,相当于一句语句
pint pa,pb;

这样pa和pb就都是int型指针了
因为宏定义只是简单的字符串代换,在预处理阶段完成
而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名,被命名的标识符具有类型定义说明的功能

无参宏注意事项

  1. 宏名一般用大写字母表示,以便于与变量区别。
  2. 宏定义末尾不必加分号,否则连分号一并替换。
#define PI 3.14;//则会将3.14;代替PI
  1. 宏定义可以嵌套。
  2. 可用#undef命令终止宏定义的作用域。
  3. 使用宏可提高程序通用性和易读性,减少不一致性,减少输入错误和便于修改。如数组大小常用宏定义。
  4. 预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,因此预处理不做语法检查。
  5. 字符串" "中永远不包含宏,否则该宏名当字符串处理。
  6. 宏定义不分配内存,变量定义分配内存。
带参数的宏

定义形式

带参宏定义中,形参不分配内存单元,因此不必作类型定义

调用形式

宏调用中的实参有具体的值,要用它们去代换形参,因此必须作类型说明,这点与函数不同。
函数中形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。
而在带参宏中只是符号代换,不存在值传递问题。

对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参

【例3】

#define ADD(X) X+10
int y=ADD(5);

在宏调用时,用实参5去代替形参x,经预处理宏展开后的语句为y=5+10

【例4:经典易错用例】

#define SQ(r) r*r

一般使用是没有问题
但是用来 area=SQ(a+b) 宏展开后为:a+ba+b 不符合本意
相比之下函数调用时会先把实参表达式的值(a+b)求出来再赋予形参r 而宏替换对实参表达式不作计算直接地照原样代换
在宏定义中,字符串内的形参通常要用括号括起来以避免出错
即area=SQ((a+b)) 宏展开(a+b)
(a+b)
进一步地,考虑到运算符优先级和结合性
遇到area=10/SQ(a+b)时即使形参加括号仍会出错
即area=10/a+b*a+b
因此,还应在宏定义中的整个字符串外加括号

综上,正确的宏定义是#define SQ( r ) (( r )*( r )) 即宏定义时建议所有的层次都要加括号。

带参宏注意事项:

  1. 宏名和形参表的括号间不能有空格。
  2. 宏替换只作替换,不做计算,不做表达式求解。
  3. 函数调用在编译后程序运行时进行,并且分配内存。
  4. 宏替换在编译前进行,不分配内存。
  5. 宏的哑实结合不存在类型,也没有类型转换。
  6. 函数只有一个返回值,利用宏则可以设法得到多个值。
  7. 宏展开使源程序变长,函数调用不会。
  8. 宏展开不占用运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值)。
  9. 为防止无限制递归展开,当宏调用自身时,不再继续展开。
    如:#define TEST(x) (x + TEST(x))被展开为1 +
    TEST(1)。
  10. #define可以定义多条语句,以替代多行的代码,但应注意替换后的形式,避免出错;宏定义在换行时要加上一个反斜杠””,而且反斜杠后面直接回车,不能有空格

参考blog1

参考blog2
https://www.cnblogs.com/clover-toeic/p/3851102.html

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4652714.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-06
下一篇 2022-11-06

发表评论

登录后才能评论

评论列表(0条)

保存