栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言——预处理(二)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言——预处理(二)

文章目录
  • #define
    • define 定义标识符
      • define中的续行符
    • define 定义宏
    • #define 替换规则
    • #和##
      • "#"
      • "##"
      • 带副作用的宏参数
    • 宏与函数对比
      • 宏与函数的命名约定
    • 预处理指令 #undef

#define define 定义标识符
#define M 100

#define 定义M,相当于给M赋值了100,在预处理阶段会把M替换成100

一下奇怪的标识符例子:

#define MAX 1000
#define reg register //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%stline:%dt 
date:%sttime:%sn" ,
__FILE__,__LINE__ , 
__DATE__,__TIME__ )
#define mian main
#define , ,
#define ( (
#define ) )
#define ture true
#define ; ;

只要加上上面5行代码,那么输入时可以减少出错

define中的续行符
#define DEBUG_PRINT printf("file:%stline:%dt 
date:%sttime:%sn" ,
__FILE__,__LINE__ , 
__DATE__,__TIME__ )

后面的反斜杠表示的是一个续行符,只有在写#define 定义标识符的时候才会出现的语法,但是值得注意的是千万不要在续行符的后面加上空格、回车之类的,如果反斜杠跟空格回车组合在一起会成转义字符,那么你的程序可能会出问题,小心使用即可

define 定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。

常见的错误用法

#include 
#define SQUARE(x) x*x//求x的平方
int main()
{
	int ret = SQUARE(4+1);
	//          4+1*4+1 =9
	printf("%dn", ret);//结果为9
	return 0;
}

正确的方式

#include 
#define SQUARE(x) ((x)*(x))//求x的平方
int main()
{
	int ret = SQUARE(4+1);
	//    (4+1)*(4+1)=25     
	printf("%dn", ret);//结果为25
	return 0;
}

所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或 邻近操作符之间不可预料的相互作用。

#define 替换规则

接下来我们用例子来理解三条替换规则

#include 
#define MAX 100
#define SQUARE(x) ((x)*(x)*MAX)
int main()
{
	int ret = SQUARE(5);
	printf("%dn", ret);
	return 0;
}

1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
例如,#define定义的宏中含有#define定义的符号MAX,则调用该宏时,首先将MAX替换。

#include 
#define SQUARE(x) ((x)*(x)*100)
int main()
{
	int ret = SQUARE(5);
	printf("%dn", ret);
	return 0;
}

2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
例如,上例中经过该步骤后,代码等价于:

#include 
int main()
{
	int ret = ((5)*(5)*100);
	printf("%dn", ret);
	return 0;
}

3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
上例不再包含任何由#define定义的符号。

注意:
1.宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

例如:

#和## “#”

使用#,把一个宏参数名变成对应的字符串
例如

#不会将参数名替换成10,而是保留参数名,将这个参数名变为字符串

上面宏定义,打印的类型固定了,那么然后例如宏来变得更加灵活呢?

这个宏的功能是可以按照指定的格式化进行输出

“##”
  • ##可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符。
    例如

这个宏的功能是,把两个标识符合并成一个吧,在进行文本替换(预处理宏替换)
所以最后打印的结果是100;

带副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,
导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
例如: x+1;不带副作用
x++; 带有副作用

MAX宏可以证明具有副作用的参数所引起的问题。

宏替换后变成这样
//int ret = ((a++)>(b++)?(a++):(b++))
由于是后置加加,先使用后加加,先进行表达式10>11?,++后变成 a=11 b=12,在进行下一条表达式 返回b,然后a和b在++,
所以结果为 返回值 12

宏与函数对比

宏经常被用来执行简单的运行,例如两数最大值

#define MAX(x,y) ((x)>(y)?(x):(y))

那么为什么不使用函数呢?

int Max(int x, int y)
{
	return x > y ? x : y;
}

  • 1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
  • 2.更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。但是宏可以适用于整形、长整型、浮点型等可以用于来比较的类型。宏是类型无关的。

而且,宏有时候可以做到函数做不到的事情。例如,宏的参数可以出现类型,但是函数却不可以。

当然和宏相比函数也有劣势的地方:

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  2. 宏是没法调试的。
  3. 宏由于类型无关,也就不够严谨。
  4. 宏可能会带来运算符优先级的问题,导致程容易出现错。
宏与函数的命名约定

宏名通常全大写, 函数名不要全都大写

预处理指令 #undef

功能:移除一个宏定义
例如,下列代码将#define定义的标识符MAX移除后,编译器便不能识别之后的MAX。

#include 
#define MAX 100
int main()
{
	printf("%dn", MAX);//正常使用
#undef MAX
	printf("%dn", MAX);//报错,MAX未定义
}

谢谢收看,下一章 命令行定义 + 条件编译 + 文件包含

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/289428.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号