深入 C 语言和程序运行原理
于航
PayPal 技术专家
21121 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
深入 C 语言和程序运行原理
15
15
1.0x
00:00/00:00
登录|注册

09|编译准备:预处理器是怎样处理程序代码的?

推荐给朋友或同事
邀请交流和讨论
合适的预处理器使用场景
宏函数与C函数的区别
C 预处理器的作用和重要性
预处理结果分析
编译命令
示例代码
预处理运算符 “#” 与 “##” 的用法
谨慎使用以避免降低代码可读性和引入BUG
合理使用以增强程序的动态扩展能力和可配置性
预处理器是“双刃剑”
封装代码模板
条件编译
定义魔数
定义完备的多语句宏函数
警惕宏函数导致的多次副作用
为宏函数的参数添加括号
为宏函数的返回值添加括号
6. 添加辅助性注释信息
5. 其他预处理指令
4. 文件包含预编译指令
3. 条件预编译指令
2. 处理宏定义
1. 删除注释
适用语言:C、C++、Objective-C
ANSI C 标准:能力加强
后续扩展:带参数的宏、条件编译
初始功能:基本的文件包含和字符串替换
引入时间:1973年左右
分享
总结
实验
思考题
使用注意事项
使用场景
宏编写技巧
预处理流程
背景知识
C 预处理器

该思维导图由 AI 生成,仅供参考

你好,我是于航。
C 预处理器是 C 标准中的另一块重要内容。对代码进行预处理,是 C 源代码在被“真正”编译,并生成机器代码前的重要一环。合理使用预处理指令,可以让源代码根据不同的环境信息进行动态变化,并生成适合在当前环境下编译的 C 代码。这里我们提到的“环境”,一般与目标操作系统、CPU 体系架构,以及当前平台上各系统库的支持情况有关。
除此之外,预处理器还为我们提供了一定的能力,可以更加高效、灵活地组织 C 源代码。比如,我们可以对一个完整的 C 程序进行结构化拆分,根据代码在语法结构或功能定位上的不同,将它们分别整理在独立的 C 源文件中。而在包含有程序入口 main 函数的源文件内,我们便可以通过 #include 预处理指令,在编译器开始真正处理 C 代码前,将程序运行所需要的其他代码依赖包含进来。
那么今天,我们就来看看有关 C 预处理器的内容。接下来,我将介绍 C 预处理器的相关背景知识、预处理的基本流程,以及宏编写技巧和使用注意事项。

C 预处理器的相关背景知识

预处理器被引入 C 标准的时间比 C 语言诞生晚了大约一年。1973 年左右,在贝尔实验室研究员 Alan Snyder 的敦促下,预处理器被正式整合至 C 语言中。它的最初版本只拥有基本的文件包含和字符串替换能力。而在此后不久,它被进一步扩展,加入了带参数的宏以及条件编译等功能。在随后发布的 ANSI C 标准中,预处理器的能力再次得到了加强。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

C语言预处理器是C标准中的重要内容,通过对源代码进行处理,使得代码可以根据不同环境信息动态变化。预处理器提供了高效、灵活地组织C源代码的能力,包括文件包含、宏定义和条件编译等功能。预处理的基本流程包括删除注释、处理宏定义、条件预编译指令和文件包含预编译指令等步骤。在编写宏函数时,需要注意避免由C运算符优先级等因素导致的问题,例如为宏函数的返回值添加括号。文章通过介绍C预处理器的背景知识、预处理的基本流程以及宏编写技巧,帮助读者了解C预处理器的相关内容。同时,强调了预处理器的合适使用场景,包括定义程序中使用到的魔数、基于特定运行环境的条件编译和封装代码模板。文章还提供了一些技巧,如为宏函数的参数添加括号、警惕宏函数导致的多次副作用以及定义完备的多语句宏函数。总之,预处理器是一把“双刃剑”,合理使用可以让程序具备更强的动态扩展能力,但任意乱用可能会导致程序源代码的可读性大大降低,甚至引入难以调试的BUG。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入 C 语言和程序运行原理》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(9)

  • 最新
  • 精选
  • LDxy
    看起来预处理器似乎是一个独立于编程语言的东西,那为何大多数语言不引入预处理器呢?这样想用宏的人可以用宏,不想用宏的人可以不用,不是更能满足程序员吗

    作者回复: 就像你说的一样,其实大部分“现代语言”都不希望将预处理器作为语言自身标准的一部分,这意味着你可以用一些第三方提供的预处理能力来对源码进行编译前的处理。总的来看,大部分人认为预处理器的过度使用通常会导致项目整体结构的混乱,且不利于代码调试。并且宏作为简单的文本替换也不具备语义上的完备性(宏定义时的形式无法决定其具体使用时产生的效果)。因此,从语言的角度来看,大家都更倾向于做编译时优化,尽量让语言本身的特性就可以满足预处理器的使用场景。个人理解哈~

    2021-12-27
    4
  • 骨汤鸡蛋面
    老师,请问下为什么c要有头文件呢

    作者回复: 实际上这个问题要从 C 的发展历史上来看,头文件的一个重要目的就是为了能够提供一套对外稳定的接口文档,供其他程序使用已经写好的 C 代码实现,以实现代码重用。当然,不要直接把它与现代语言比如 Go、Python、Rust 等具有包管理器能力的语言相比。

    2022-04-20
    2
    2
  • x
    #define _STR(x) #x // 将参数x变为字符串 #define _CONN(x,y) x##y // 将x和y参数连接在一起, (1,2)--> 12
    2022-09-18归属地:广东
    5
  • 小爷
    ##的作用是连接的作用;而#的作用是就是把参数当作字符串代替。
    2022-03-07
    5
  • 贺志鹏
    #: 将宏的参数转换为字符串,这个操作称为"stringization"。 ##:将两个Token粘合成一个Token。
    2022-02-24
    2
  • linker
    ## 用来连接字符串 # 操作符#在字符串中输出实参
    2021-12-27
    1
  • 白花风信子
    ##有次在重构自己的小作业用过,有点类似在预处理中预处理的感觉qwq。
    2021-12-27
    1
  • Geek_00a01d
    思考题 交作业 使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.
    2022-12-20归属地:河南
  • =
    #可以用来进行宏定义与替换、文件包含、条件编译、错误生成、行控制、预定义名称 ##可以用来进行token的联结
    2022-01-08
收起评论
显示
设置
留言
9
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部