预处理详解

目录

1、预定义符号

2、#define定义常量

3、#define定义宏

4、带有副作用的宏参数

5、宏的替换规则

6、宏和函数的对比

​ 7、#和##

8、命名约定

9、#undef

10、条件编译

11、头文件的包含方式

12、嵌套文件包含


1、预定义符号

C语言设置了一些预定义符号,可以在预处理阶段直接使用:

1 FILE //进行编译的源文件

2 LINE //文件当前的行号

3 DATE //文件被编译的日期

4 TIME //文件被编译的时间

5 STDC //如果编译器遵循ANSI C,其值为1,否则未定义

比如:

2、#define定义常量

这个比较简单,和我们的用法差不多,

#define MAX 100

#define REG register

当我们定义的语句比较长,可以分成多行来写,比如:

#define DEBUG_PRINT printf("file:%s\tline:%d\t \

date:%s\ttime:%s\n" ,\

FILE,LINE , \

DATE,TIME )

在每行的末尾处加上反斜杠 \ ,也叫续行符,最后一行不用加

在定义#define时,最后不用加 ; 容易出现语法错误,

3、#define定义宏

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

宏的声明方式:

#define name( parament-list ) stuff

其中parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中,

注意:

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

例如:

注意,#define只是完成了替换,这时候又一个问题出现了,运算符优先级问题.比如:

为了避免这种问题,我们要对每个x加上 (),这样就解决问题了,可能比较麻烦

#define SQUARE(x) ((x)*(x))

这样写是最安全的,可以避免由于运算符优先级出现的问题

4、带有副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候的时候,如果宏参数带有副作用,那么在使用这个宏的时候就会出现一些不好的后果,副作⽤就是表达式求值的时候出现的永久性效果。

x+1 //没有副作用

x++ //有副作用

比如:

这是不带副作用的情况下,a和b的值都不变

这时候我们要注意,尽量避免这样写

5、宏的替换规则

1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号,如果是,他们先替换

2.替换文本被插入到程序中原来的位置中,这些都是在预处理阶段就完成的

3.最后,再次对文件进行扫描,看看是否还有包含#define定义的符号,如果是,就重复上述步骤;

注意:

1.宏参数和宏定义中可以出现其他宏定义的符号,但是不能出现递归;

2.当预处理器搜索#define定义的符号时,字符串常量的内容并不被搜索;

比如:

字符串中的M并不被搜索

6、宏和函数的对比

了解一下7、

7、#和##

#运算符将宏的一个参数转换成字符串字面量,他仅允许出现在带参数的替换列表中

#运算符执行的操作可以理解为"字符串化"

比如:

##运算符可以把位于他两边的符号合成一个符号,##被称为记号粘合,对于这个东西用的少,能看懂被人用的什么意思就好

比如:

看看就好,用的不多

8、命名约定

平常的习惯是宏名全部大写

函数名不要全部大写

9、#undef

这条指令用于移除一个宏定义

#undef NAME

//如果现存的⼀个名字需要被重新定义,那么它的旧名字⾸先要被移除。

比如:

我们要修改M的值,要先移除这个宏,然后再重新定义;

10、条件编译

在编译一个程序的时候我们如果要将一条语句编译或者放弃的时候,可以用条件编译指令

比如:

常见的条件编译指令:

1.单分支的条件编译

#if 常量表达式 //如果表达式语句为真,则执行#if里面的语句,否则不执行

//语句

#endif

2.多分支的条件编译:

#if 常量表达式

//语句

#elif 常量表达式

//语句

#elif 常量表达式

//语句

#else

//语句

#endif

比如:

只有第一个表达式为真,所以只执行第一条语句,如果有多个表达式都为真, 则只执行最先为真的那一个表达式

3.判断是否被定义

#ifdef 宏常量

//语句

#endif

他还·等价于

#if defined(宏常量)

//语句

#ednif

如果宏常量被定义,那么就执行条件里面的语句,否则不执行,比如:

红框框里的两条语句时等价的;

如果__DEBUG__被定义,则执行语句,否则不执行

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

11、头文件的包含方式

本地文件包含:

#include"filename.h"

先在源文件所在目录下查找,如果找不到,就在标准库头文件里面查找,如果还找不到,就提示编译错误;

库文件包含:

#include<stdio.h>

查找库函数头文件直接在标准库里面去查找,如果找不到就提示编译错误

如果是这样,那么标准库头文件也可以用" " 来包含?

是这样的,但是这样查找效率低下,而且也不容易区分是库文件还是本地文件

12、嵌套文件包含

我们已经知道#include指令可以使另一个文件被编译,就是在#include这行指令处展开被编译的文件,但是如果我们重复包含,对编译的压力就会比较大,运行起来比较慢

如何解决头文件被重复包含的问题?

条件编译

在每个头文件的开头写:

#ifndef TEST_H

#define TEST_H

//语句

#endif

意思是:

如果 TEST_H 没有被定义,那么 #define TEST_H 定义,然后执行头文件的内容,

如果包含的有相同的头文件,先判断 #ifndef TEST_H 发现 TEST_H 被定义了,那么不再执行头文件中的内容;

另外还有一种更加方便的写法:

#pragma once

作用和上面是一样的,都是防止头文件被重复包含。

相关推荐
FeboReigns2 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns2 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++
_小柏_2 小时前
C/C++基础知识复习(43)
c语言·开发语言·c++
yoyobravery3 小时前
c语言大一期末复习
c语言·开发语言·算法
落羽的落羽7 小时前
【落羽的落羽 C语言篇】自定义类型——结构体
c语言
Kisorge7 小时前
【C语言】代码BUG排查方式
c语言·开发语言·bug
yoyo勰8 小时前
sqlite3
c语言·sqlite
就爱学编程9 小时前
重生之我在异世界学编程之C语言:数据在内存中的存储篇(上)
c语言·数据结构
意疏9 小时前
【C 语言指针篇】指针的灵动舞步与内存的神秘疆域:于 C 编程世界中领略指针艺术的奇幻华章
c语言·开发语言·指针
带多刺的玫瑰9 小时前
Leecode刷题C语言之考场就座
c语言·前端·javascript