预处理
文章目录
1 预处理
在编译之前进行的处理,处理以 # 开头的指令,包括拷贝#include包含的文件代码,替换#define定义的宏,条件编译#if。
2 文件包含
2.1 #include指令中 < > 和 “ ” 的区別
对于 #include <filename.h>
,编译器先从标准库路径开始搜索 filename.h,
对于 #include “filename.h”
,编译器先从用户的工作路径开始搜索 filename.h,
2.2 头文件的作用
-
通过头文件来调用库函数。
-
头文件能加强类型安全检查。
3 宏定义
3.1 宏和函数的区别
- 宏替换只作替换,不做计算,不做表达式求解;而函数调用需要。
- 宏替换在编译前进行,不分配内存;函数调用在编译后程序运行时进行,且分配内存。
- 宏定义不存在类型问题,没有类型转换;而函数有。
- 宏展开会使源程序变长,而函数调用 不会。
- 宏展开不占运行事件,只占编译时间;函数调用占运行时间。
3.2 #define的缺陷
宏定义在预处理阶段进行,主要做的是字符替换工作。
- 无法进行类型检查。
- 由于优先级的不同,使用宏定义时,可能会存在副作用。
- 无法单步调试。
- 会导致代码膨胀,由于宏定义是文本替换,需要对代码进行展开,相比较函数调用的方式,会存在较多的冗余代码。
3.3 typedef和define的区别
typedef与define都是替一个对象取一个别名,以此来增强程序的可读性。
原理不同:#define是预处理指令,只进行字符串替换,不作类型检查;typedef是关键字,在编译时处理,有类型检查的功能。
功能不同:#define不仅可以为类型取别名,还可以定义常量、变量、编译开关等;typedef用来定义类型的别名,包括内部类型和用户自定义类型。
作用域不同:#define没有作用域限制;typedef有自己的作用域。
void fun(){
#define A int
typedef int B;
}
int main()
{
A a = 5; //正确,可以使用A,因为宏替换没有作用域
B b = 0; //错误,不能使用B,但一般不在函数内定义typedef
}
对指针操作不同:两者修饰指针类型时,作用不同。
#define IPTR1 int*
typedef int* IPTR2;
int main()
{
INTPTR1 pl,p2; //字符串替换后为int* p1,p2;声明了一个指针变量p1和整型变量p2
INTPTR2 p3,p4; //p3、p4都为指针变量
}
3.4 #define和const的区别
define既可以替代常数值,又可以替代表达式,甚至是代码段,但是容易出错,而const的引入可以增强程序的可读性,它使程序的维护与调试变得更加方便。
- define只是用来进行单纯的文本替换,不分配内存空间,它存在于程序的代码段;const常量存在于程序的数据段,并在堆栈中分配了空间,并且可以被调用、传递。
- define常量没有数据类型;const常量有数据类型,编译器可以对const常量进行类型检查。
3.5 使用#define声明1年中有多少秒(忽略闰年问题)
#define SECOND_PER_YEAR (365*24*60*60)UL
考虑可能存在数据溢出问题,使用长整型。
3.6 宏定义返回两数中的最大值
#define MAX(a,b) ((a) > (b) ? (a) : (b))
3.7 判断一个数是无符号数还是有符号数
对于值而言,若这个数及其求反后的值都大于0,则该数为无符号数,反之为有符号数。
#define ISUNSIGNED(a) (a>=0 && ~a>=0)
3.8 解释作用
#define TRACE(S) (printf("%s\n",#S))
3.9 不使用第三个变量,交换两个变量的值
#define SWAP(a,b) a=a+b,b=a-b,a=a-b
4 条件编译
4.1 防止头文件被重复包含
使用条件编译
#ifndef __MY_HEAD_H__
#define __MY_HEAD_H__
...
#endif
4 条件编译
4.1 防止头文件被重复包含
使用条件编译
#ifndef __MY_HEAD_H__
#define __MY_HEAD_H__
...
#endif