当前位置:   article > 正文

【C语言】预处理详解_预处理 c语言

预处理 c语言

一、预定义符号

__ FILE__ //进行编译的源文件
__ LINE__ //文件当前的行号
__ DATE__ //文件被编译的日期
__ TIME__ //文件被编译的时间
__ STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
这些符号是C语言内置的

二、#define定义

请添加图片描述

2.1.#define定义标识符

定义符号进行文本替换

#define MAX 100
#define con const
#define MIN 0

int main()
{
	int a = MAX;// 100

	con int b = MIN;// const int b = 0

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

需不需要加;呢?
不需要。
如果加了;那么在进行替换时,就会将;一同替换过去,就会有空语句的赘余。

2.2.#define定义宏

定义方式同函数类似

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

int main()
{
	printf("%d\n",MAX(1, 5));//输出5

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意:
对于宏的参数是直接进行表达式的替换而不是传参,有可能出现符号优先级的问题。
所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中
的操作符或邻近操作符之间不可预料的相互作用。

2.3.#define替换规则

1.如果有多次符号先替换内存的符号
2.对于宏参数的替换是直接表达式的替换
3.最后对文件进行扫描,如果还含有#define定义的符号,则重复以上过程
注意:
1.#define定义时可以出现其他符号,但不能递归
2.字符串里面的符号不会被替换

2.4.#和##

(1)#号可以将宏中的参数转化为对应字符串

看看这样的代码
在这里插入图片描述
可以发现字符串是具有自动连接功能的;

我们可以这样定义宏
在这里插入图片描述
#号可以将宏中的参数转化为对应字符串
在这里插入图片描述

(2)## 把位于符号两边的符号合成一个符号

在这里插入图片描述

2.5.带副作用的宏参数

有可能会多次改变参数值的宏
比如说:
x + 1:没有副作用
x++:有副作用

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?
  • 1
  • 2
  • 3
  • 4
  • 5

编译器处理后:z = ( (x++) > (y++) ? (x++) : (y++));
结果:x=6 y=10 z=9

2.6.宏和函数对比

宏通常用于简单的计算
为什么不用函数?

1.用于函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多,函数的调用,计算,函数的返回,而宏是直接进行替换,所以在程序的规模和速度上更胜一筹。(通过反汇编可以看到代码的执行效率)
2.更为重要的是函数的参数必须声明为特定的类型,所以函数只能在类型适合的表达式上使用。反之宏可以适用于长整型,整型,浮点型等。
宏是类型无关的

宏的缺点

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

我们可以从以下几个角度来进行对比:

1、代码长度
2、执行速度
3、操作符优先级
4、带副作用的参数
5,参数类型
6、调试
7、递归

2.7.命名约定

1、宏名全大写
2、函数名不全大写

三、#undef

#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
  • 1
  • 2

四、命令行定义

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程
请添加图片描述
请添加图片描述

五、条件编译

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

#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

六、文件包含

6.1.头文件被包含的方式

我们已经知道, #include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方
一样。
这种替换的方式很简单:
预处理器先删除这条指令,并用包含文件的内容替换。
这样一个源文件被包含10次,那就实际被编译10次。

相当于就是每包含一次这个头文件,这个头文件中包含的内容就会复制粘贴第多少次在当前这个文件中

”“:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标
准位置查找头文件。
<>:查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。

6.2.嵌套头文件包含

我们可以使用条件编译去防止反复包含的问题
解决方法一:

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif   //__TEST_H__
  • 1
  • 2
  • 3
  • 4

解决方法二:

#program once
  • 1

这两种方法都可以避免头文件的反复包含

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/347686
推荐阅读
相关标签
  

闽ICP备14008679号