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

C语言的include没你想的那么简单(图文版)C 语言中的 include 很简单 但不是你想象中的简单 你对 include 的认识是不是只停留在包含头文件的认知中 好像也没有别的用处 小小东西也翻不起什么风浪 include stdio h include user header h bala bala stdio h

大家好,我是讯享网,很高兴认识大家。

C语言中的include很简单,但不是你想象中的简单。

#include <stdio.h> #include "user_header.h" // bala bala

讯享网

人家是这么用的

讯享网# 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_8BIT # include "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的东西。

讯享网// 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.h extern int add(int a, int b);
讯享网// minus.c int 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; }
// main.c int main(void) {     #include "squel.xx"     int g = squel(2,2); return 0; }
讯享网// data.txt 1,2,3,4,5,6,7,8,9
// main.c int arr[] = { #include "data.txt" } int main(void) { return 0; }

好吧……

这好办,动动手指头,一个gcc -E命令即可搞定。就以上面第一个例子,命令行执行gcc ./main.c -E -o main.i

讯享网# 0 ".\\main.c" # 0 "<built-in>" # 0 "<命令行>" # 1 ".\\main.c" # 1 "add.h" 1 extern int add(int a, int b); # 3 ".\\main.c" 2 # 1 "minus.c" 1 int 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; }
// 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里的内容混乱,头文件里的内容全部是全局的了。

因为,预编译还有更好玩的玩法。

因为重复include,就相当于把头文件重复展开了多次,C语言中有些定义是不允许重复多次的。例如,上面的例子

讯享网// main.c #include "add.h" #include "minus.c" #include "minus.c"
In file included from .\main.c:4: minus.c:1:5: 错误:‘minus’重定义 1 | int minus(int a, int b) | ^~~~~
讯享网#ifndef _MINUS_ #define _MINUS_ int minus(int a, int b) { return a-b; } #endif

这就不得不提下,我以前写的X-MACRO大法了。

以下是一个MEMORY字段分配的设想:

  1. 将Memory的物理地址映射到自定义逻辑地址
  2. 逻辑地址按Memory的Block对齐,逻辑地址从0开始
  3. 用户数据按逻辑地址分配
  4. 应用接口按实际内容大小操作
  5. 底层接口根据逻辑地址对齐读写Memory
entry name address
size
ID_DATA1 0
8
ID_DATA2 8
8
ID_DATA3 16
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是个很酷的玩法哦,欢迎查阅和讨论。

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

f43d287987cda67e6cbf554fd289f9f0.png

5356669c7ddecf404222471bdaa96299.jpeg

546b2ed74441ff12f67b2f97ee3ea6e7.jpeg

小讯
上一篇 2025-03-20 12:07
下一篇 2025-02-24 11:05

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/124094.html