预处理详解

简介: 预处理详解

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


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

相关文章
|
7月前
|
编译器 C++
C 预处理器
C 预处理器。
91 10
|
2月前
|
编译器 Linux C语言
|
7月前
|
编译器 C语言
预处理深入
预处理深入
43 0
预处理深入
|
7月前
|
编译器 C语言
c预处理器
c预处理器
48 0
|
7月前
|
Linux C语言 Windows
C预处理分析
C预处理分析
48 2
|
7月前
|
编译器 C++
c++预处理器
c++预处理器
42 0
|
安全 编译器 C语言
详解预处理(1)
详解预处理(1)
83 1
|
编译器 Linux C语言
详解预处理(2)
详解预处理(2)
80 0
预处理的学习
预处理的学习
61 0