#
号开头的命令称为预处理命令;在编译之前对源文件进行简单加工的过程,就称为 预处理(即预先处理、提前处理)。
如
#include //引入头文件
#define MAX 100 //宏定义
C语言开发者们编写了很多常用函数,并分门别类的放在了不同的文件,这些文件就称为头文件(header file)。每个头文件中都包含了若干个功能类似的函数,调用某个函数时,要引入对应的头文件,否则编译器找不到函数。
实际上,头文件往往只包含类的声明和函数的声明,也就是告诉我们类和函数怎么用,而函数定义可以保存在其他文件中,在链接时才会找到。
头文件 .h
尽量包含类、变量和函数的声明,不包含定义;其他源文件 .cpp
包含函数和变量的定义;主程序源文件 main.c
包含主函数。
例子:
ROS/C++创建自己的类库(头文件和源文件)
引入头文件使用#include
命令,并将文件名放在< >
中,#include 和 < > 之间可以有空格,也可以没有。
头文件以.h
为后缀(新的C++标准库无后缀),而C语言代码文件以.c
为后缀,它们都是文本文件,没有本质上的区别,#include 命令的作用也仅仅是将头文件中的文本复制到当前文件,然后和当前文件一起编译。你可以尝试将头文件中的内容复制到当前文件,那样也可以不引入头文件。
#include 的用法有两种,如下所示:
#include
#include "myHeader.h"
使用尖括号< >
和双引号" "
的区别在于头文件的搜索路径不同:
< >
,编译器会到系统路径下查找头文件;" "
,编译器首先在当前目录下查找头文件,如果没有找到,再到系统路径下查找。也就是说,使用双引号比使用尖括号多了一个查找路径,它的功能更为强大。
前面我们一直使用尖括号来引入标准头文件,现在我们也可以使用双引号了,如下所示:
#include "stdio.h"
#include "stdlib.h"
stdio.h 和 stdlib.h 都是标准头文件,它们存放于系统路径下,所以使用尖括号和双引号都能够成功引入;而我们自己编写的头文件,一般存放于当前项目的路径下,所以不能使用尖括号,只能使用双引号。
建议:使用尖括号来引入标准头文件,使用双引号来引入自定义头文件(自己编写的头文件),这样一眼就能看出头文件的区别
宏调用只是简单的字符串替换
#define 叫做宏定义命令,它也是C语言预处理命令的一种。所谓宏定义,就是用一个标识符来表示一个字符串(广义,可以是数字、表达式、if 语句、函数等),如果在后面的代码中出现了该标识符,那么就全部替换成指定的字符串。
常见用法
#define 宏名 字符串
//如
#define N 100
#define M (n*n+3*n) //一定要带括号,且后面没有分号
宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef
命令。
#define PI 3.14159
int main(){
// Code
return 0;
}
#undef PI
void func(){
// Code
}
表示 PI 只在 main() 函数中有效,在 func() 中无效。
宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名,在宏展开时由预处理程序层层代换。例如:
#define PI 3.1415926
#define S PI*y*y /* PI是已定义的宏名*/
对语句:
printf("%f", S);
在宏代换后变为:
printf("%f", 3.1415926*y*y);
习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母
宏定义可带参数**(用起来很复杂,容易出错),不建议使用**
在宏定义中的参数称为“形式参数”,在宏调用中的参数称为“实际参数”,这点和函数有些类似。
对带参数的宏,在展开过程中不仅要进行字符串替换,还要用实参去替换形参。
对于带参宏定义不仅要在参数两侧加括号,还应该在整个字符串外加括号。
#define M(y) ((y)*(y)+3*(y)) //宏定义
// TODO:
k=M(5); //宏调用
在宏展开时,用实参 5 去代替形参 y,经预处理程序展开后的语句为k=5*5+3*5
。
宏调用只是简单的字符串替换
#define M1(x) x*x+2*x
#define M2(x) (x)*(x)+2*(x)
#define M3(x) ((x)*(x)+2*(x))
在以下运算中有不同的结果
int a;
M1(a+1)=a+1*a+1+2*a+1=4*a+2;
M2(a+1)=(a+1)*(a+1)+2*(a+1)=a*a+4*a+3;
100/M2(a+1)=100/(a+1)*(a+1)+2*(a+1);
100/M3(a+1)=100/((a+1)*(a+1)+2*(a+1));
能够根据不同情况编译不同代码、产生不同目标文件的机制,称为条件编译.(属于预处理步骤)
http://c.biancheng.net/view/1986.html
预处理指令是以#
号开头的代码行,# 号必须是该行除了任何空白字符外的第一个字符。# 后是指令关键字,在关键字和 # 号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。
下面是本章涉及到的部分预处理指令:
指令 | 说明 |
---|---|
# | 空指令,无任何效果 |
#include | 包含一个源代码文件 |
#define | 定义宏 |
#undef | 取消已定义的宏 |
#if | 如果给定条件为真,则编译下面代码 |
#ifdef | 如果宏已经定义,则编译下面代码 |
#ifndef | 如果宏没有定义,则编译下面代码 |
#elif | 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个#if……#else条件编译块 |
预处理功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的,程序员在程序中用预处理命令来调用这些功能。
宏定义可以带有参数,宏调用时是以实参代换形参,而不是“值传送”。
为了避免宏代换时发生错误,宏定义中的字符串应加括号,字符串中出现的形式参数两边也应加括号。
文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。
条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。
使用预处理功能便于程序的修改、阅读、移植和调试,也便于实现模块化程序设计。