编译前奏:预处理全面梳理

个人主页流年如夢

专栏《C语言》

文章目录

Ladies and gentlemen,本篇文章讲的是 预处理全过程、#define 宏、#与##、条件编译、头文件包含等 核心知识点;全程高能,不容错过!!!

前言

预处理是C程序编译前的文本替换与处理阶段,不生成可执行代码,只对源文件做文本层面的替换、展开、删除、包含等工作;掌握预处理,能写出更简洁、更易维护、更少BUG 的代码

一.预定义符号

C语言内置预处理符号,可以直接使用:

  1. __FILE__ --> 当前源文件名
  2. __LINE__ --> 当前行号
  3. __DATE__ --> 编译日期
  4. __TIME__ --> 编译时间
  5. __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++));

运行结果

五.宏替换的规则

  1. 先检查参数,替换其中已定义的宏
  2. 接着替换文本插入原位置
  3. 然后再次扫描,继续替换(宏不能递归

👉需要注意的是++字符串常量内的内容不被替换且** 宏不能递归定义**++

六.宏和函数的对比

对比 函数
代码长度 每次调用都插入,代码变长 只一份,调用跳转
执行速度 快(无需调用) 稍慢
优先级问题 易出错,必须加括号 无问题
副作用参数 危险,多次求值 安全,只求值一次
参数类型 无类型限制 类型严格
调试 不可调试 可逐行调试
递归 不可递归 可递归

👉宏的优势在于可以接收类型做参数,但函数做不到:

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两种包含方式

  1. #include "filename" --> 先找当前目录,再找标准库
  2. #include <filename> --> 直接找标准库路径

12.2避免头文件重复包含

12.2.1通用方法

c 复制代码
#ifndef __TEST_H__
#define __TEST_H__

//内容

#endif

12.2.2个别编译器支持

c 复制代码
#pragma once

十三.其他预处理指令

#error --> 编译时报错
#pragma --> 向编译器发送指令
#line --> 重置行号

🎯总结

  1. 预处理是纯文本处理,不涉及语法、语义
  2. 宏是高效替换,必须全程加括号
  3. 宏参数有副作用(++--)会引发严重问题
  4. #字符串化,##记号粘合
  5. 条件编译用于跨平台、调试、裁剪代码
  6. 头文件必须加保护,避免重复包含
  7. 宏比函数快,但更危险、难调

⚠️易错点

  1. 宏定义不加括号,优先级出错
  2. 宏参数带副作用++--
  3. 常量定义末尾多写分号
  4. 分不清#include <>""的区别
  5. 忘记头文件保护,导致重复包含
  6. 用feof判断循环结束,逻辑错误

👀 关注 我们一路同行,从入门到大师,慢慢沉淀、稳步成长
❤️ 点赞 鼓励原创,让优质内容被更多人看见
⭐ 收藏 收好核心知识点与实战技巧,需要时随时查阅
💬 评论 分享你的疑问或踩坑经历,一起交流避坑、共同进步

相关推荐
freshman_y13 小时前
一篇介绍C语言中二级指针和二维数组的文章
c语言·开发语言
weixin_4139206114 小时前
LVGL仪表显示项目
c语言
小柯博客15 小时前
STM32MP2安全启动技术深度解析
c语言·c++·stm32·嵌入式硬件·安全·开源·github
爱编码的小八嘎16 小时前
C语言完美演绎9-1
c语言
꧁细听勿语情꧂16 小时前
合并两个有序表、判断链表的回文结构、相交链表、环的链表一和二
c语言·开发语言·数据结构·算法
气宇轩昂固执狂17 小时前
01-初识C语言
c语言·开发语言
我要升天!17 小时前
C语言连接 MySQL:libmysqlclient 获取方式详解
c语言·开发语言·数据库·mysql·adb
潜创微科技18 小时前
CH9245:双 Type‑C 转 PD 芯片方案,便携显示与拓展坞的理想选择
c语言·开发语言
leo__52019 小时前
基于时延的麦克风声源定位 - C实现
c语言·开发语言·算法