1. 发生时机
预处理操作,不是c语言语句,因此语句末尾没有分号,在预处理阶段完成,本质是替换操作
发生时段:
2. 宏
2.1. 不带参宏
2.1.1. 宏常量
#define定义的宏,只能在一行内表达(换行符表示结束而非空格),如果想多行表达,则需要加续行符
#define PI 3.14\
15926535
int main()
{
printf("%f", PI); // 3.1415926535
return 0;
}
宏常量,常被const/enum变量取代,用于定义文件路径则被常用
#define FILEPATH "/Users/mingqi/Desktop/my_code/c:c++/file.c"
宏常量的缺陷
#define N 2 + 3;
// #define N (2 + 3);
int main()
{
int num = N * 2;
printf("num = %d", num); // 8 10
return 0;
}
解决这些问题,要不吝惜使用括号
2.1.2. 宏类型
宏可以给类型起别名,因其缺点,常被typedef取代
#define CHARP char *
int main()
{
CHARP p, q;
printf("p = %d, q = %d\n", sizeof(p), sizeof(q)); // 8 1
return 0;
}
2.2. 带参宏(宏函数)
常将短小精悍的函数进行宏化,这样可以嵌入到代码中,减少调用的开销。但是代价就是编译出的文件可能会变大
宏函数常常一行表达不完,如多行,采用续行符进行接续
2.2.1. 宏函数
#define S(a, b) a*b
area = S(3,2);
// 宏展开:area = 3 * 2;
// 注:宏名和参数间不能用空格
#define POWER(x) x*x
x = 4; y = 6;
z = POWER(x, y);
// 宏展开:z = x+y * x+y;
// 一般写成:#define POWER(x) ((x) * (x))
// 宏展开:z = ((x+y) * (x+y))
使用define,别吝惜括号
常见宏函数
#define MAX(a, b) (a>b) ? a : b
2.2.2. 宏出错处理函数
#define F_PRINT_ERR(e) \
do \
{ \
if (e == NULL) \
{ \
printf("open error\n"); \
exit(-1); \
} \
} while (0)
2.3. 取消宏
#define MAX 23
int main()
{
printf("%d\n", MAX); // 23
#undef MAX
printf("%d\n", MAX); // ERROR
return 0;
}
3. 条件编译
依据条件,判断哪些程序段参与编译
3.1. 单双路(#ifdef / #ifndef #else #endif)
#if #else #endif
#ifdef #ifndef #else #endif
#define X86
int main()
{
#ifdef X86
printf("xxxxx");
#else
printf("aaaa");
#endif
return 0;
}
3.2. 单双多路(#if #elif #endif)
#define X86 1
#define MIPS 2
#define POWERPC 3
#define MACHINE POWERPC
int main()
{
#if MACHINE == X86
printf("hello 86");
#elif MACHINE == MIPS
printf("hello mips");
#elif MACHINE == POWERPC
printf("hello powerpc");
#endif
return 0;
}
3.3. 编译期指定宏 gcc -D
4. 头文件包含(#include)
4.1. 包含的意义
全写入,被包含的文件中
包含是支持嵌套的
4.2. 包含的方式
4.2.1. 方式<>
#include <stdio.h>,从系统指定路径中搜索包含头文件,linux中的系统路径为(/usr/include)
#include <stdio.h> -> #include "stdio.h" -> #include <stdio.h>
4.2.2. 方式" "
#include “myString.h”,从工程当前路径中搜索包含头文件,如果当前工程路径下没有,则到系统路径下搜索包含
#include "myString.h" -> #include <myString.h>
4.3. 多文件编程
4.3.1. 多文件编程意义
多文件编程的好处:
- 方便管理,协同开发
- 便于分享与加密(作成函数库)
4.3.2. 多文件编程的前提
C语言是以文件为单位进行编译的,编译器只需要函数声明即可,链接阶段提供实现就可以完成生成可执行文件
4.4. 定义头文件
4.4.1. 头文件自包含
.c文件中存在相互调用的关系,自包含可以免去了多余的前向声明
5. 其他
5.1. #运算符 利用宏创建字符串
将替换符字符串化,解决字符串中,不可被替换的参数问题。
// #define str(x) #x
// #define str(x) "aaaaaxaaaaa"
#define str(x) "aaaaa" #x "aaaaa"
#define printSqr(x) printf("sqr(" #x ") = %d \n", x *x);
int main()
{
printf("%s\n", str(100)); // aaaaa100aaaaa
printSqr(5); // sqr(5) = 25
return 0;
}
5.2. ##运算符 预处理的粘合剂
解决了参数变量与宏展开,无法一一对应的问题
// #define sum(a, b) (aa + bb)
#define sum(a, b) (a##a + b##b)
#define XNAME(n) x##n
int main()
{
printf("%d\n", sum(2, 3)); // 55
int XNAME(1) = 14;
printf("x1 = %d\n", XNAME(1)); // 14
return 0;
}
#define PRINT_XN(n) printf("x" #n " = %d\n", x##n)
int main()
{
int XNAME(1) = 14;
int XNAME(2) = 28;
printf("x1=%d, x2=%d\n", XNAME(1), XNAME(2)); // x1=14, x2=28
PRINT_XN(1);
PRINT_XN(2);
return 0;
}
5.3. 预定义宏
DATE:进行预处理的日期("MMmm dd yyyy"形式的字符串蚊子)
FILE:代表当前源代码文件名的字符串蚊子
LINE:代表当前源代码中的行号的整数常量
TIME:源文件编译时间,格式:“hh:mm:ss”
func:当前所在函数名
在打印调试信息时打印这两个宏__FILE__和__LINE__可以给开发者非常有用的提示
void why_me();
int main()
{
printf("The file is %s.\n", __FILE__);
printf("The date is %s.\n", __DATE__);
printf("The time is %s.\n", __TIME__);
printf("The line is %d.\n", __LINE__);
printf("The func is %s.\n", __func__);
why_me();
return 0;
}
void why_me()
{
printf("The func is %s.\n", __func__);
printf("The file is %s.\n", __FILE__);
printf("The line is %d.\n", __LINE__);
}