【C语言】预处理指令有哪些

一、#define 详解

1. #define 定义标识符

#define name stuff

本质:将代码中的所有name替换成stuff

如果定义的 stuff 过长,可以分成几行写。
但是除了最后一行,每行后面都要加一个 \(续行符) 。
比如:
在这里插入图片描述

2. #define 定义宏

利用 #define 把参数替换到文本中,这种实现称为宏。

#define name( parament-list ) stuff

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

3. #undef 用法

该指令用于移除 #define 的定义。

比如:

#define N 10
#undef N

4. #define 易错点

  • 下面提问:在 #define 定义标识符的时候,要不要在最后加上 ;
    答:建议不要,容易出现问题。
    比如下面的场景:
    在这里插入图片描述
    这里的MAX在预处理阶段替换成1000;
    所以,i = MAX;预处理后就变成了i = 1000;;
    这样多了一个;,表示在 if 语句后面的一个空语句,导致悬空 else。

  • #define 的本质是替换,所以不会先进行计算。
    比如:
    在这里插入图片描述
    如上图所示,输出结果是5*2+3=13,而非5*5=25
    因此,为了保证使用不出错,建议多加括号。
    特别是宏,每个都加,整体再加
    比如:

#define DOUBLE( x)  ( ( x ) + ( x ) )

5. #define 替换规则

  1. 调用宏时,如果宏中有已经被定义的标识符,要先替换。
  2. 宏不能递归。
  3. 预处理器查找 #define 定义的符号时,不会查找字符串里的内容。

6. 宏与函数对比

先说结论:对于简单的运算,用宏更好

比如求两个数中的较大值:

#define MAX(a, b) ((a)>(b)?(a):(b))
  1. 宏的优点:
  • 更快。
    • 函数存在调用和返回的额外开销。
  • 宏的参数与类型无关,更自由。
    • 比如,宏的参数可以是类型:
      在这里插入图片描述
  1. 宏的缺点:
  • 宏无法调试。
  • 宏与类型无关,不够严谨。
  • 宏可能会有运算符优先级的问题,容易出错。
    • 比如传入++a这样的内容。

命名规定:

  • 宏全部大写。
  • 函数名不要全部大写。

二、条件编译

条件编译,即条件为真则编译,否则跳过不编译。

常见的条件编译指令:

#if 常量表达式
//...
#endif

#if#endif是一对,成对出现。

举个例子
在这里插入图片描述
此时条件为真,屏幕上成功打印出内容。

在这里插入图片描述
此时条件为假,在VS中,main函数失去高光,不编译。

条件编译还可以有多个分支。

#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif

除了上面的指令之外,条件编译还可以判断某符号是否被#define定义。

#ifdef N
//..
#endif


#ifndef N
//..
#endif

#ifdef#endif也是成对出现的。
上述代码中,如果N被定义,则#ifdef判断条件为真,否则为假。
#ifndef恰好相反。
还有一种写法,与之完全等价。

#if defined(N)//#ifdef N
//...
#endif


#if !defined(N)//#ifndef N
//...
#endif

三、文件包含

库文件包含:直接去标准路径查找。

#include <filename.h>

本地文件包含:先在源文件所在目录查找,没找到再去标准路径查找。

#include "filename"

于是我们发现,库文件似乎也可以使用 "" 的形式包含。
但是这样做查找的效率就会低一些,因为要先在源文件所在目录查找,然后没找到,再去标准路径查找。而且,这样也区分不了是库文件还是本地文件,代码可读性较低。

四、实用小技巧

在写项目的时候,我们有时会不可避免地重复包含头文件,这样就导致同一个头文件进行了多次编译,造成资源浪费。
事实上,我们可以用条件编译的方式来避免多次编译:

#ifndef 头文件名
#define 头文件名
//头文件内容
#endif

另一种方法是避免头文件重复引入:

#pragma once

版权声明:本文为m0_74121788原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。