C语言的include没你想的那么简单(图文版)

原创 嵌入式软件实战派 2023-06-07 08:39
C语言中的include很简单,但不是你想象中的简单。
(如果你不想看以下文字内容,可以查看本文对应的视频版)
你对#include的认识是不是只停留在包含头文件的认知中,好像也没有别的用处,小小东西也翻不起什么风浪?
#include #include "user_header.h"// bala bala
#include就是包含头文件用的,不是吗?!
我之前也一直这么认为的,直到我看了某些大神写的代码,后来我还特意查阅了C99标准。
人家是这么用的
define DET_START_SEC_VAR_INIT_UNSPECIFIED# include "MemMap.h" 
# define DET_STOP_SEC_VAR_INIT_UNSPECIFIED# include "MemMap.h"
# define DET_START_SEC_VAR_NOINIT_8BITinclude "MemMap.h" 
# define DET_STOP_SEC_VAR_NOINIT_8BIT# include "MemMap.h"
还有这样用的
#define STRUCT_GEN_START
#include "defines.h"#include "param_gen.h"
#include "defines.h"#include "param_gen.h"
#include "defines.h"#include "param_gen.h"
#include "defines.h"#include "param_gen.h"
#include "defines.h"#include "param_gen.h"
当时,看得我一愣一愣的……
其实,简单来说,#include就是“包含”某个文件的意思,但这个“”,不能将思维限死在“头文件”这个概念中,而应该有更多的想象!
#include在C语言中,算是预编译指令(preprocessing directive)范畴,而预编译指令在C语言就是一个大学问了。
但是,我们先不要被这个“编译指令”名称绕晕。上文,我们提到了头文件这个概念,当然我们也知道还有一个叫源文件的概念。这些我就不解释了。但是,在C99标准中有一段这样的话,需要研究下:

source file together with all the headers and source files included via the preprocessing directive #include is known as a preprocessing translation unit. After preprocessing, a preprocessing translation unit is called a translation unit.

ISO/IEC 9899:1999 (E)
简单地理解,一个source file和一些由#include包含着的headers和source files,通过预编译后,变成一个叫translation unit的东西。
从这里可以看出来,#include不但可以包含headers,还可以包含source files。
所以,我下面这个#include "add.h"#include "minus.c"都是正确的,编译一点问题都没有。
// main.c#include "add.h"#include "minus.c"
int add(int a, int b){ return a+b;}
int main(void){ int c = add(1,2); int d = minus(2-1); return 0;}
// add.hextern int add(int a, int b);
// minus.cint minus(int a, int b){    return a-b;}
不妨将脑洞开大一点,除了*.h和*.c文件,我还可以include点别的么?答:可以。例如
// main.c#include "multiply.txt"
int main(void){    int e = multiply(2,2); return 0;}
甚至,这样也行
// main.c#include "devide.fxxk"
int main(void){    int f = devide(2,2); return 0;}
继续啊,#include不是放在文件上方,放中间行么。当然
// main.cint main(void){    #include "squel.xx"    int g = squel(2,2);    return 0;}
好家伙,这么下去,我是不是可以这么干
// data.txt1,2,3,4,5,6,7,8,9
// main.cint arr[] = {    #include "data.txt"}
int main(void){ return 0;}
然后,你又好奇了,能不能将data.txt换成二进制形式的data.bin?
呵呵,这种不行,编译器在预编译阶段只认得是text文本才行。
好吧……
你不是说这是个预编译指令吗,我很好奇,#include预编译后成啥样子的?
这好办,动动手指头,一个gcc -E命令即可搞定。就以上面第一个例子,命令行执行gcc ./main.c -E -o main.i
# 0 ".\\main.c"# 0 ""# 0 "<命令行>"# 1 ".\\main.c"
# 1 "add.h" 1extern int add(int a, int b);# 3 ".\\main.c" 2# 1 "minus.c" 1int minus(int a, int b){ return a-b;}# 4 ".\\main.c" 2
int add(int a, int b){ return a+b;}
int main(void){ int c = add(1,2); int d = minus(2-1); return 0;}
看到了吧,#include就是把它后面的文件内容直接include进来。就这么简单粗暴。
那么#include在C语言中是不是很简单?
你说呢!
我见过有人这么写代码的,还TM的一整个团队是这么做的。
将整个所以.h文件全部包含在一个includes.h的头文件中,然后在其他.c文件里面,就直接#include "includes.h"。
// includes.h#include "adc.h"#include "uart.h"#include "spi.h"#include "iic.h"#include "dma.h"#include "pwm.h"#include "pin.h"#include "led.h"#include "os.h"#include "timer.h"...
TM的简便
我第一次见到这玩意,简直是惊呆了,还有这种操作。
不好吗?有什么不好?多简洁啊!
从上面的分析看,#include就是将它后面包含的头文件源文件,全部展开哦。
简洁?你问过编译器啥感受么?
带来的最直接的感受是,编译过程慢!includes.h里包含得越多就越慢!
另外一个隐含的问题是,会造成include里的内容混乱,头文件里的内容全部是全局的了。
我绝对不推荐这种玩法的。
因为,预编译还有更好玩的玩法。
不过,在介绍新玩法之前,得想个问题,如果一个头文件,重复包含多次会怎样?
也许,你会回答,我是不允许出现这种情况的,就算出现这种情况,我也可以用#ifdef...#endif这种方式规避。
如果你是应届生面试,这样回答,面试官也许是点点头说你有点经验的。
因为重复include,就相当于把头文件重复展开了多次,C语言中有些定义是不允许重复多次的。例如,上面的例子
// main.c#include "add.h"#include "minus.c"#include "minus.c"
这样是有问题的,因为上面相当于重复定义了两次int minus(int a, int b)函数了。
In file included from .\main.c:4:minus.c:1:5: 错误:‘minus’重定义    1 | int minus(int a, int b)      |     ^~~~~
如果将minus.c改成这样就行了
#ifndef _MINUS_#define _MINUS_int minus(int a, int b){    return a-b;}#endif
这个简单啊,我也会啊。
嗯,但是,我不是想说这个,我真的想说重复include有意想不到的好处呢。
这就不得不提下,我以前写的X-MACRO大法了。
以下是一个MEMORY字段分配的设想:
  1. Memory

  2. MemoryBlock0

  3. Memory

我想定义一些内容条目,这些条目分别对应不同的内存地址,不同的长度,以后有需要还可以继续从后面添加就这样:
entry nameaddress
size
ID_DATA10
8
ID_DATA28
8
ID_DATA316
16
...


可以在一个头文件里面做这样的定义
// defines.h#ifdef ENTRY_ID  #define ENTRY(id,addr,size) id,  #undef ENTRY  #undef ENTRY_ID#endif
#ifdef ENTRY_ADDR #define ENTRY(id,addr,size) addr, #undef ENTRY #undef ENTRY_ADDR#endif
#ifdef ENTRY_SIZE #define ENTRY(id,addr,size) size, #undef ENTRY #undef ENTRY_SIZE#endif
接着在C文件里面这么玩
// memory.c#define ALL_ENTRIES()       \    ENTRY(ID_DATA1, 0, 8)   \    ENTRY(ID_DATA2, 8, 8)   \    ENTRY(ID_DATA3, 16, 16) \    ENTRY(ID_DATA4, 32, 8)
#define ENTRY_ID#include "defines.h"typedef enum{ ALL_ENTRIES() MEM_ID_MAX} MEM_ID;
#define ENTRY_ADDR#include "defines.h"const uint32_t mem_addr[] ={ ALL_ENTRIES()};
#define ENTRY_SIZE#include "defines.h"const uint16_t mem_size[] ={ ALL_ENTRIES()};
你也许会反问我,定义一个结构体不就搞定了吗?
别急,这样做的好处是enum的ID顺序跟addr和size是一一对应的,不会错乱,另一个好处是,可以随便在ALL_ENTRIES()下面扩展条目,也不影响ID的对应关系。
如果用结构体去定义的话,也很好,但是会增加数组遍历时间,如果是很庞大的条目数的话,这个效率问题就要考虑了。
其实,对上面的做法,我还做了优化,写在了这两篇文章中,X-MACRO是个很酷的玩法哦,欢迎查阅和讨论。
《宏的高级用法——X-MACRO》
《宏定义X-MACRO的高级应用(高阶版,含源码)》

关注公众号“嵌入式软件实战派”,回复关键字include即可获得本文测试源码。

如果你喜欢我的文章,请关注,并转发点赞在看,这是对我莫大的鼓励!

嵌入式软件实战派 专注嵌入式软件开发领域知识传授,包括C语言精粹,RTOS原理与使用,MCU驱动开发,AUTOSAR搭建,软件架构方法设计等。
评论 (0)
  • 引言随着物联网和智能设备的快速发展,语音交互技术逐渐成为提升用户体验的核心功能之一。在此背景下,WT588E02B-8S语音芯片,凭借其创新的远程更新(OTA)功能、灵活定制能力及高集成度设计,成为智能设备语音方案的优选。本文将从技术特性、远程更新机制及典型应用场景三方面,解析该芯片的技术优势与实际应用价值。一、WT588E02B-8S语音芯片的核心技术特性高性能硬件架构WT588E02B-8S采用16位DSP内核,内部振荡频率达32MHz,支持16位PWM/DAC输出,可直接驱动8Ω/0.5W
    广州唯创电子 2025-04-01 08:38 133浏览
  • 据先科电子官方信息,其产品包装标签将于2024年5月1日进行全面升级。作为电子元器件行业资讯平台,大鱼芯城为您梳理本次变更的核心内容及影响:一、标签变更核心要点标签整合与环保优化变更前:卷盘、内盒及外箱需分别粘贴2张标签(含独立环保标识)。变更后:环保标识(RoHS/HAF/PbF)整合至单张标签,减少重复贴标流程。标签尺寸调整卷盘/内盒标签:尺寸由5030mm升级至**8040mm**,信息展示更清晰。外箱标签:尺寸统一为8040mm(原7040mm),提升一致性。关键信息新增新增LOT批次编
    大鱼芯城 2025-04-01 15:02 152浏览
  • 职场之路并非一帆风顺,从初入职场的新人成长为团队中不可或缺的骨干,背后需要经历一系列内在的蜕变。许多人误以为只需努力工作便能顺利晋升,其实核心在于思维方式的更新。走出舒适区、打破旧有框架,正是让自己与众不同的重要法宝。在这条道路上,你不只需要扎实的技能,更需要敏锐的观察力、不断自省的精神和前瞻的格局。今天,就来聊聊那改变命运的三大思维转变,让你在职场上稳步前行。工作初期,总会遇到各式各样的难题。最初,我们习惯于围绕手头任务来制定计划,专注于眼前的目标。然而,职场的竞争从来不是单打独斗,而是团队协
    优思学院 2025-04-01 17:29 93浏览
  • 升职这件事,说到底不是单纯靠“干得多”或者“喊得响”。你可能也看过不少人,能力一般,甚至没你努力,却升得飞快;而你,日复一日地拼命干活,升职这两个字却始终离你有点远。这种“不公平”的感觉,其实在很多职场人心里都曾经出现过。但你有没有想过,问题可能就藏在一些你“没当回事”的小细节里?今天,我们就来聊聊你升职总是比别人慢,可能是因为这三个被你忽略的小细节。第一:你做得多,但说得少你可能是那种“默默付出型”的员工。项目来了接着干,困难来了顶上去,别人不愿意做的事情你都做了。但问题是,这些事情你做了,却
    优思学院 2025-03-31 14:58 101浏览
  • 北京贞光科技有限公司作为紫光同芯产品的官方代理商,为客户提供车规安全芯片的硬件、软件SDK销售及专业技术服务,并且可以安排技术人员现场支持客户的选型和定制需求。在全球汽车电子市场竞争日益激烈的背景下,中国芯片厂商正通过与国际领先企业的深度合作,加速融入全球技术生态体系。近日,紫光同芯与德国HighTec达成的战略合作标志着国产高端车规芯片在国际化道路上迈出了关键一步,为中国汽车电子产业的发展注入了新的活力。全栈技术融合:打造国际化开发平台紫光同芯与HighTec共同宣布,HighTec汽车级编译
    贞光科技 2025-03-31 14:44 121浏览
  •        在“软件定义汽车”的时代浪潮下,车载软件的重要性日益凸显,软件在整车成本中的比重逐步攀升,已成为汽车智能化、网联化、电动化发展的核心驱动力。车载软件的质量直接关系到车辆的安全性、可靠性以及用户体验,因此,构建一套科学、严谨、高效的车载软件研发流程,确保软件质量的稳定性和可控性,已成为行业共识和迫切需求。       作为汽车电子系统领域的杰出企业,经纬恒润深刻理解车载软件研发的复杂性和挑战性,致力于为O
    经纬恒润 2025-03-31 16:48 82浏览
  • 文/郭楚妤编辑/cc孙聪颖‍不久前,中国发展高层论坛 2025 年年会(CDF)刚刚落下帷幕。本次年会围绕 “全面释放发展动能,共促全球经济稳定增长” 这一主题,吸引了全球各界目光,众多重磅嘉宾的出席与发言成为舆论焦点。其中,韩国三星集团会长李在镕时隔两年的访华之行,更是引发广泛热议。一直以来,李在镕给外界的印象是不苟言笑。然而,在论坛开幕前一天,李在镕却意外打破固有形象。3 月 22 日,李在镕与高通公司总裁安蒙一同现身北京小米汽车工厂。小米方面极为重视此次会面,CEO 雷军亲自接待,小米副董
    华尔街科技眼 2025-04-01 19:39 81浏览
  • 文/Leon编辑/cc孙聪颖‍步入 2025 年,国家进一步加大促消费、扩内需的政策力度,家电国补政策将持续贯穿全年。这一利好举措,为行业发展注入强劲的增长动力。(详情见:2025:消费提振要靠国补还是“看不见的手”?)但与此同时,也对家电企业在战略规划、产品打造以及市场营销等多个维度,提出了更为严苛的要求。在刚刚落幕的中国家电及消费电子博览会(AWE)上,家电行业的竞争呈现出胶着的态势,各大品牌为在激烈的市场竞争中脱颖而出,纷纷加大产品研发投入,积极推出新产品,试图提升产品附加值与市场竞争力。
    华尔街科技眼 2025-04-01 19:49 81浏览
  • REACH和RoHS欧盟两项重要的环保法规有什么区别?适用范围有哪些?如何办理?REACH和RoHS是欧盟两项重要的环保法规,主要区别如下:一、核心定义与目标RoHS全称为《关于限制在电子电器设备中使用某些有害成分的指令》,旨在限制电子电器产品中的铅(Pb)、汞(Hg)、镉(Cd)、六价铬(Cr6+)、多溴联苯(PBBs)和多溴二苯醚(PBDEs)共6种物质,通过限制特定材料使用保障健康和环境安全REACH全称为《化学品的注册、评估、授权和限制》,覆盖欧盟市场所有化学品(食品和药品除外),通过登
    张工13144450251 2025-03-31 21:18 105浏览
  • 在智能交互设备快速发展的今天,语音芯片作为人机交互的核心组件,其性能直接影响用户体验与产品竞争力。WT588F02B-8S语音芯片,凭借其静态功耗<5μA的卓越低功耗特性,成为物联网、智能家居、工业自动化等领域的理想选择,为设备赋予“听得懂、说得清”的智能化能力。一、核心优势:低功耗与高性能的完美结合超低待机功耗WT588F02B-8S在休眠模式下待机电流仅为5μA以下,显著延长了电池供电设备的续航能力。例如,在电子锁、气体检测仪等需长期待机的场景中,用户无需频繁更换电池,降低了维护成本。灵活的
    广州唯创电子 2025-04-02 08:34 50浏览
  • 引言在语音芯片设计中,输出电路的设计直接影响音频质量与系统稳定性。WT588系列语音芯片(如WT588F02B、WT588F02A/04A/08A等),因其高集成度与灵活性被广泛应用于智能设备。然而,不同型号在硬件设计上存在关键差异,尤其是DAC加功放输出电路的配置要求。本文将从硬件架构、电路设计要点及选型建议三方面,解析WT588F02B与F02A/04A/08A的核心区别,帮助开发者高效完成产品设计。一、核心硬件差异对比WT588F02B与F02A/04A/08A系列芯片均支持PWM直推喇叭
    广州唯创电子 2025-04-01 08:53 145浏览
  • 提到“质量”这两个字,我们不会忘记那些奠定基础的大师们:休哈特、戴明、朱兰、克劳士比、费根堡姆、石川馨、田口玄一……正是他们的思想和实践,构筑了现代质量管理的核心体系,也深远影响了无数企业和管理者。今天,就让我们一同致敬这些质量管理的先驱!(最近流行『吉卜力风格』AI插图,我们也来玩玩用『吉卜力风格』重绘质量大师画象)1. 休哈特:统计质量控制的奠基者沃尔特·A·休哈特,美国工程师、统计学家,被誉为“统计质量控制之父”。1924年,他提出世界上第一张控制图,并于1931年出版《产品制造质量的经济
    优思学院 2025-04-01 14:02 105浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦