预处理详解

本文详细介绍了C语言中的预处理指令#define,包括定义标识符、宏、替换规则,以及#和##的用法。同时,讲解了带副作用的宏参数和宏与函数的区别。此外,还探讨了条件编译如#if...#endif的使用,以及头文件的包含策略,强调了防止头文件重复包含的重要性。
摘要由CSDN通过智能技术生成

目录

1.预定义符号

2.#define

2.1#define定义标识符

2.2#define定义宏 

2.3#define替换规则 

2.4#和## 

#的用法

##的用法

2.5带副作用的宏参数 

3.宏和函数的对比

命名约定:

4.#undef

5.条件编译

5.1#if...#endif

5. 2多分支条件编译

5. 3判断是否被编译

 5.4嵌套指令

6.头文件的包含

6.1防止头文件重复包含

6.2本地文件和库文件

查找策略不同


1.预定义符号

__FILE__
__LINE__
__DATE__
__TIME__
__STDC__
//进行编译的源文件
//文件当前的行号
//文件被编译的日期
//文件被编译的时间
//如果编译器遵循ANSI C,其值为1,否则未定义

这些预定义符号都是语言内置的。
直接应用就可以,例子:

2.#define

2.1#define定义标识符

这里使用#define定义一个标识符M(注意空格)为50,打印M时,M直接会替换成50 。

#define定义标识符在预处理阶段,在程序运行之前,因此定义时应在头文件之前。

且预处理中,标识符会直接替换成空格后的元素,是直接替换,要注意括号的使用,例如:

2.2#define定义宏 

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

实现举例:

 这里建议在写宏的内容时,加上相应的括号,如((x)>(y)?(x):(y)),因为我们的目的是为了求两个参数的最大值,宏的文本替换不加括号存在优先级的问题,就像上述标识符一样,为了确保效率和安全性,要注意宏的内容加上相应的括号

2.3#define替换规则 

1. 在调用宏时,看看参数是否包含任何由#define定义的符号。若有,则被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
1. 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

2.4#和## 

#的用法

把一个宏参数变成对应的字符串

如何把参数插入到字符串中?
例如:

 那么这里就可以用到宏加 # 来实现:

 在PRINT 中,ab会替换n,也会替换 #n,这里要注意 #n 前后都是字符串

代码如下:

#define PRINT(n) printf("the value of "#n" is %d\n",n)
#include <stdio.h>
int main()
{
	int a = 20;
	PRINT(a);
	int b = 50;
	PRINT(b);
	return 0;
}

##的用法

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

#define CAT(B,a) B##a
#include <stdio.h>
int main()
{
	int Bossage = 50;
	printf("%d\n",CAT(Boss,age));
	return 0;
}

 

2.5带副作用的宏参数 

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

举个例子:

 副作用会带来永久性影响:

#define MAX(x,y) ((x)>(y)?(x):(y))
#include <stdio.h>
int main()
{
	int a = 4;
	int b = 6;
	int max = MAX(a++,b++);
	printf("%d\n",max);
	printf("%d %d\n",a,b);
	return 0;
}

 

3.宏和函数的对比

宏通常被应用于执行简单的运算。函数用来执行较为复杂的运算。

宏不能被调试,并且参数无类型,但执行更快

函数运行操作:函数调用+逻辑运算+函数返回

宏的操作:逻辑运算

具体区别如下:

属 性#define定义宏函数
代 码 长 度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码
执 行 速 度更快存在函数的调用和返回的额外开销,所以相对慢一些
操 作 符 优 先 级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号。函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。
带 有 副 作 用 的 参 数参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一次,结果更容易控制。
参 数 类 型宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。函数的参数是与类型有关的,如
果参数的类型不同,就需要不同
的函数,即使他们执行的任务是
不同的。
调 试宏是不方便调试的函数是可以逐语句调试的
递 归宏是不能递归的函数是可以递归的

命名约定:

函数和宏的语法相似,为了便于区别,建议:

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

4.#undef

用于移除一个宏定义

#define MAX(x,y) ((x)>(y)?(x):(y))
#include <stdio.h>
int main()
{
	int a = 4;
	int b = 6;
#undef MAX
	int max = MAX(a, b);
	printf("%d\n", max);
	return 0;
}

 移除后的宏无法被解析

5.条件编译

5.1#if...#endif

#if 常量表达式

#endif

 

5. 2多分支条件编译

#if 常量表达式

      //...

#elif 常量表达式     (可理解为#else if)

     //...

#else

     //...

#endif

用法跟if ...else 语句相似,只是应用在了宏上,常量表达式是对宏的条件。

5. 3判断是否被编译

#if defined(symbol)
#ifdef symbol

上两行效果相同,下两行效果相同
#if !defined(symbol)
#ifndef symbol

都与#endif成对使用

 5.4嵌套指令

将这些指令当作if else 语句使用就可以,这里给出语法模板:

#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.头文件的包含

6.1防止头文件重复包含

防止头文件重复包含,可以在声明头文件时加上以下预处理语句:

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

或者
#pragma once

//头文件内容


6.2本地文件和库文件

本地文件就是自己创建的函数包含时用"文件名":#include "文件名"

库文件包含时用<.h>:#include <stdio.h>

查找策略不同

本地文件:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件,如果找不到就提示编译错误。

库文件:查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。

注:包含库文件也可用" ",这样做查找的效率就低些,也不容易区分是库文件还是本地文件了,不建议这样做。

 

  • 21
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今年依旧去年春

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值