预处理命令
C语言源文件要经过编译、链接才能生成可执行程序,在编译之前对源文件进行简单加工的过程,就称为预处理(即预先处理、提前处理)。
C语言的预处理命令均以#开头。
种类:
1、宏定义: #define
2、文件包含: #include
3、条件编译: #if—#else----#endif等
格式:
1、#开头
2、占单独书写行
3、句尾不加分号
宏定义
1、不带参数的宏定义
格式:#define 标识符 字符串
功能:用指定标识符(宏名)代替字符串
作用域:从定义命令到文件结束
#undef 宏名 可以终止宏名作用域
宏展开:预编译时,用宏体替换宏名(不做语法检查)
注意:(1)宏名中不允许有空格、而且必须遵守C变量命名规则,宏名一般用大写字母;
(2)一般在程序开头;
(3)宏定义可以嵌套,但不可以递归;
(4)代码中的宏名如果被引号包围,那么预处理程序不对其作宏代替;
(5)可用宏定义表示数据类型,使书写方便
(6)宏定义中使用必要的括号(),如下:
#define
#define
var = LENGHT * 2;
宏展开:var = 80+40*2;
#define
#define
var = LENGHT * 2;
宏展开:var = (80+40)*2;
2、带参数的宏定义
格式:#define 宏名(参数表) 字符串 ,例如:
#define
area = S(2,3);
宏展开: area = 2*3;
注意:
(1) 带参宏定义中,形参之间可以出现空格,但是宏名和形参列表之间不能有空格出现。
(2)在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型。而在宏调用中,实参包含了具体的数据,要用它们去替换形参,因此实参必须要指明数据类型。
这一点和函数是不同的:在函数中,形参和实参是两个不同的变量,都有自己的作用域,调用时要把实参的值传递给形参;而在带参数的宏中,只是符号的替换,不存在值传递的问题。
(3)在宏定义中,字符串内的形参通常要用括号括起来以避免出错。
对于带参宏定义不仅要在参数两侧加括号,还应该在整个字符串外加括号。如下:
#define
带参宏和函数的区别
include标签
使用尖括号< >和双引号" “的区别在于头文件的搜索路径不同:
使用尖括号< >,编译器会到系统路径下查找头文件;
而使用双引号” ",编译器首先在当前目录下查找头文件,如果没有找到,再到系统路径下查找。
也就是说,使用双引号比使用尖括号多了一个查找路径,它的功能更为强大。
#include预处理指令所插入的文件,通常文件扩展名是.h,并且文件内部不外乎是函数原型、宏定义、类型定义等。不管是标准头文件,还是自定义头文件,都只能包含变量和函数的声明,不能包含定义,否则在多次引入时会引起重复定义错误。(函数、变量的定义在.c文件中)。
原因参考:头文件不能包含定义的原因
头文件包含的内容:
1、类型定义
2、函数原型
3、内联函数声明
4、全局数据声明
5、常量定义
6、包含指令
7、宏定义
8、注释
头文件不能包含内容:
1、一般函数定义
2、数据定义
使用xx时,只需要引入xx.h文件,不需要引入xx.c。
程序编译的时候,并不会去找b.c文件中的函数实现,只有在link的时候才进行这个工作。我们在b.c或c.cpp中用#include "a.h"实际上是引入相关声明,使得编译可以通过,程序并不关心实现是在哪里,是怎么实现的。源文件编译后成生了目标文件(.o或.obj文件),目标文件中,这些函数和变量就视作一个个符号。在link的时候,需要在makefile里面说明需要连接哪个.o或.obj文件(在这里是b.c生成的.o或.obj文件),此时,连接器会去这个.o或.obj文件中找在b.c中实现的函数,再把他们build到makefile中指定的那个可以执行文件中。
条件编译
功能:根据指定的标识符是否被定义过,确定在程序编译阶段编译哪一段程序段;
格式1:
#if
程序段1
#else
程序段2
#endif
格式2:
#ifdef//该标识符被定义过
程序段1
#else
程序段2
#endif
格式3:
#ifndef//该标识符未被定义过
程序段1
#else
程序段2
#endif