关于预处理相关的知识详解

预处理

预定义符号

FILE //进行编译的源文件
LINE //文件当前的行号
DATE //文件被编译的日期
TIME //文件被编译的时间
STDC //如果编译器遵循ANSI C,其值为1,否则未定义

如下图所示:
在这里插入图片描述

define

#define 定义标识符
语法形式:

#define name stuff

有一个例子:

#define CASE break;case

我们把CASE定义成break;case后,用switch语句的时候就可以写成这样:

switch(n)
{
	case 1:
	CASE 2;
	CASE3;
}

但是最好不要这样写,有点故意炫技的感觉,也让别人看不懂代码.
另外:

如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。

#define DEBUG_PRINT printf("file:%s\tline:%d\t \
                          date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )

这里问大家一个问题:在define定义标识符的时候,要不要在最后加上 ‘;’
这里建议大家不要加上,如下面这个例子:

#define MAX 1000;
#define MAX 1000
if(condition)
 max = MAX;
else
 max = 0;

会出现语法错误.

#define 定义宏

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

#define name( 这里面放的是参数) stuff

这里需要注意:
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

#define SQUARE( x ) x * x

在这里插入图片描述
我们计算3的平方,结果算出来了5,其实编译器是这样计算的:2+1*2+1
解决方法也很简单,在宏定义加上两个括号就解决了:
在这里插入图片描述
下面继续看一个宏定义:#define DOUBLE(x) (x) + (x)
在这里插入图片描述
这里为什么加了括号但是运算结果还是不对呢?
其实真实的运算是这样的:10 * (5) + (5)
这个问题的解决办法是在宏定义表达式两边加上一对括号就可以了。
通过这两个例子我们就知道了:

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

#define 替换规则:

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先
    被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上
    述处理过程。
    注意:

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

在这里插入图片描述
通过打印我们知道字符串是可以自动连接的.
所以我们想把参数传到字符串中就有思路了:
在这里插入图片描述
这里相当于把这个字符串分成了”the value is“ format “\n”

另外一个方式我们可以用#来把宏里面的参数变成字符串
在这里插入图片描述
代码中的 #VALUE 会预处理器处理为:“VALUE” .

## 的作用

##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。
什么意思呢?

在这里插入图片描述
接下来我们继续来了解带副作用的宏参数

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

在这里插入图片描述
这里的运行逻辑是这样的,5++<8++,所以执行后面的内容,而这里后置加加先使用再加加,x现在就是6,y现在是9,执行后面的条件也是先执行y再加1变成10,而赋给z的值就是9
这是预定义执行的结果:z = ( (x++) > (y++) ? (x++) : (y++));
那我们清楚了宏之后会有疑问,感觉宏和函数很相似呢?接下来我们就比较它们

宏和函数对比:

在运行速度方面:因为函数需要调用和返回,而宏在预处理阶段直接进行替换,所以宏比函数在程序的规模和速度方面更胜一筹。
**在类型方面:**宏是类型无关的,所以使用函数比宏更加的严谨,安全。
而宏还有下面的缺点:宏可能会带来运算符优先级的问题,导致程容易出现错。
每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序
的长度。宏是没法调试的。
但是宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。比如
在这里插入图片描述

命名约定

把宏名全部大写
函数名不要全部大写

undef

这条指令用于移除一个宏定义。
如果一个名字需要重定义的时候我们就可以用#undef

条件编译

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
下面是常见的条件编译指令:

1.
#if 常量表达式
 //...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
 //..
#endif
2.多个分支的条件编译
#if 常量表达式
 //...
#elif 常量表达式
 //...
#else
 //...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)
 #ifdef OPTION1
 unix_version_option1();
 #endif
 #ifdef OPTION2
 unix_version_option2();
 #endif
#elif defined(OS_MSDOS)
 #ifdef OPTION2
 msdos_version_option2();
 #endif
#endif

文件包含

简单区分一下用" "包含头文件和用< >包含头文件的区别
用" "先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标
准位置查找头文件。如果找不到就提示编译错误。
用< >查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。

那如果一个程序包含了多个头文件,而每个头文件中又包含了相同的头文件该怎么办呢?这就是嵌套包含
解决这个问题也很简单,就用我们上面讲过的条件编译:
在每个头文件的开头写:

#ifndef STU
#define STU
//头文件的内容
#endif

还有一种更简单的方式:

#pragma once

就可以避免头文件的重复引用

——————————————————————————————————————
OK,以上就是关于预处理相关的知识讲解,如有错误,欢迎指正,谢谢大家!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值