
个人主页 : 流年如夢
专栏 : 《C语言》
文章目录
- 一.预定义符号
- [二.#define 定义常量](#define 定义常量)
- [三.#define 定义宏](#define 定义宏)
- 四.带有副作用的宏参数
- 五.宏替换的规则
- 六.宏和函数的对比
- 七.#和##运算
-
- [7.1# --> 字符串化](# --> 字符串化)
- [7.2## --> 记号粘合](## --> 记号粘合)
- 八.命名约定
- 九.#undef(即取消宏定义)
- 十.命令行定义
- 十一.条件编译
- 十二.头文件的包含
- 十三.其他预处理指令
- 🎯总结
- ⚠️易错点
Ladies and gentlemen,本篇文章讲的是 预处理全过程、#define 宏、#与##、条件编译、头文件包含等 核心知识点;全程高能,不容错过!!!
前言
预处理是C程序编译前的文本替换与处理阶段,不生成可执行代码,只对源文件做文本层面的替换、展开、删除、包含等工作;掌握预处理,能写出更简洁、更易维护、更少BUG 的代码
一.预定义符号
C语言内置预处理符号,可以直接使用:
__FILE__--> 当前源文件名__LINE__--> 当前行号__DATE__--> 编译日期__TIME__--> 编译时间__STDC__--> 遵循 ANSI C 则为1
例如:
c
#include <stdio.h>
int main()
{
printf("当前文件:%s\n", __FILE__);
printf("当前行号:%d\n", __LINE__);
printf("编译日期:%s\n", __DATE__);
printf("编译时间:%s\n", __TIME__);
return 0;
}
运行结果:

二.#define 定义常量
#defin的语法:
c
#define name stuff
例如:
c
#define MAX 1000
#define DEBUG_PRINT printf("file:%s line:%d", __FILE__, __LINE__)
注意❗ :++定义常量不要加分号 ;,当宏替换后会多出分号 ;++
错误示例:
c
#define MAX 1000;//<--这里多了分号
三.#define 定义宏
宏 = 带参数的文本替换
宏的语法:
c
#define name(parameter-list) stuff
其中++左括号必须紧跟宏名,不能有空格++
错误示例:
c
#define SQUARE(x) x * x
🧐分析 :调用SQUARE(5+1)会替换为5+1*5+1=11,结果并非36,而是11
正确示例:
c
#define SQUARE(x) ((x)*(x))
四.带有副作用的宏参数
副作用 --> 参数被多次求值
例如:
c
#define MAX(a,b) ((a)>(b)?(a):(b))
c
int x=5, y=8;
int z = MAX(x++, y++);
替换后变成👉:
c
z = ((x++)>(y++)?(x++):(y++));
运行结果:

五.宏替换的规则
- 先检查参数,替换其中已定义的宏
- 接着替换文本插入原位置
- 然后再次扫描,继续替换(宏不能递归)
👉需要注意的是++字符串常量内的内容不被替换且** 宏不能递归定义**++
六.宏和函数的对比
| 对比 | 宏 | 函数 |
|---|---|---|
| 代码长度 | 每次调用都插入,代码变长 | 只一份,调用跳转 |
| 执行速度 | 快(无需调用) | 稍慢 |
| 优先级问题 | 易出错,必须加括号 | 无问题 |
| 副作用参数 | 危险,多次求值 | 安全,只求值一次 |
| 参数类型 | 无类型限制 | 类型严格 |
| 调试 | 不可调试 | 可逐行调试 |
| 递归 | 不可递归 | 可递归 |
👉宏的优势在于可以接收类型做参数,但函数做不到:
c
#define MALLOC(num, type) (type*)malloc((num)*sizeof(type))
七.#和##运算
7.1# --> 字符串化
用#把宏参数变成字符串:
c
#define PRINT(n) printf("the value of " #n " is %d\n", n)
其中调用PRINT(a)替换为:
c
printf("the value of " "a" " is %d\n", a);
7.2## --> 记号粘合
就是把两个符号合成一个标识符:
c
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ return x>y?x:y; }
再使用:
c
GENERIC_MAX(int)
GENERIC_MAX(float)
最后生成👉:
c
int int_max(int x,int y);
float float_max(float x,float y);
八.命名约定
这个简单,宏名全大写,函数名不全大写(这是为了便于区分,避免误用)
九.#undef(即取消宏定义)
取消宏定义:
c
#undef MAX
用于重新定义宏
十.命令行定义
编译时指定宏,不修改代码(在gcc编译器里)
c
gcc -D ARRAY_SIZE=10 test.c
在程序中(vs2022)可以直接使用:
c
int arr[ARRAY_SIZE];
十一.条件编译
条件编译即按条件决定是否编译某段代码,常用于调试、跨平台
常用的指令:
c
#if//-->条件为真则编译
#elif//-->否则如果
#else//-->否则
#endif//-->结束
#ifdef SYMBOL//-->已定义则编译
#ifndef SYMBOL//-->未定义则编译
例如(调试日志开关):
c
#define __DEBUG__ 1//这里1为真
#if __DEBUG__
printf("debug info\n");
#endif
十二.头文件的包含
12.1两种包含方式
#include "filename"--> 先找当前目录,再找标准库#include <filename>--> 直接找标准库路径
12.2避免头文件重复包含
12.2.1通用方法
c
#ifndef __TEST_H__
#define __TEST_H__
//内容
#endif
12.2.2个别编译器支持
c
#pragma once
十三.其他预处理指令
#error --> 编译时报错
#pragma --> 向编译器发送指令
#line --> 重置行号
🎯总结
- 预处理是纯文本处理,不涉及语法、语义
- 宏是高效替换,必须全程加括号
- 宏参数有副作用(
++、--)会引发严重问题 #字符串化,##记号粘合- 条件编译用于跨平台、调试、裁剪代码
- 头文件必须加保护,避免重复包含
- 宏比函数快,但更危险、难调
⚠️易错点
- 宏定义不加括号,优先级出错
- 宏参数带副作用 (
++、--)- 常量定义末尾多写分号
- 分不清
#include <>和""的区别- 忘记头文件保护,导致重复包含
- 用feof判断循环结束,逻辑错误
👀 关注 我们一路同行,从入门到大师,慢慢沉淀、稳步成长
❤️ 点赞 鼓励原创,让优质内容被更多人看见
⭐ 收藏 收好核心知识点与实战技巧,需要时随时查阅
💬 评论 分享你的疑问或踩坑经历,一起交流避坑、共同进步