C 语言头文件作用的简单理解

news/发布时间2024/8/25 16:56:37

C 语言是一种先声明后使用的语言。

举个例子:

如果你要在 main() 函数里调用一个你的函数 foo(),那么你有两种写法:

  1. foo() 的定义写在 main() 之前。此时 foo() 的声明和定义是同时发生的:

    int foo() {...
    }int main() {foo();
    }
    
  2. foo() 的定义写在 main() 之后。此时 foo() 的声明必须出现在被 main() 调用之前:

    int foo();int main() {foo();
    }int foo() {...
    }
    

实际上,我们只要保证在 foo()main() 调用之前声明 foo() 就好了。无论是写法 1 还是写法 2,foo() 的声明都是在被 main() 调用之前发生的。


对于单文件 C 项目来讲这样还好说。然而当我们项目中的代码越来越多之后,把所有代码放到单个文件里会使我们的项目变得难以维护。这时我们就需要把代码拆分,把功能相近的代码放到一个文件里,功能不同的代码分别放到不同的文件里。这样有利于我们后期对项目的维护。

比如说,我可以在 main.c 文件中只写 main() 函数,而把我的其他函数写到一个单独的文件 foo.c 中,就像这样:

// main.c
int main() {...
}
// foo.c
int foo1 {...
}int foo2 {...
}

然而这里有一个问题:我们如何在 main() 函数中调用 foo.c 文件中的函数呢?

首先有一个笨方法,就是你在调用 main() 函数之前手动加上 foo.c 文件中函数的声明:

int foo1();
int foo2();int main() {foo1();foo2();
}

然后我们编译的时候,两个文件都要编译并链接:

cc -c main.c
cc -c foo.c

这将生成目标文件 main.ofoo.o,接下来我们再将这两个文件链接:

cc foo.o main.o -o program

这样就生成了可执行程序 program


如果我们只用到两个函数,这样也不算麻烦。然而现实中我们可能要调用成百上千个函数,这样一来这种方法就有些过于麻烦了。

那么有没有一种方法能一次声明所有函数?

首先我们介绍一下 #include 预处理指令。它的功能是将一个文件的内容插入到这个 #include 指令所在的位置。

那我们只要把 foo.c 文件的内容插入到 main.c 文件中 main() 函数之前的位置不就好了?就像这样:

#include "foo.c"int main() {foo1();foo2();
}

我们让编译器对 main.c 文件进行预处理:

cc -E main.c

编译器输出的内容是这样的:

int foo1() {...
}int foo2() {...
}int main() {foo1();foo2()
}

可以看到,#include 指令将 foo.c 文件的内容原封不动地插入到了 main.c 中。

如果要构建项目,我们只需要编译 main.c 就够了。因为预处理阶段已经把 foo.c 的内容全部加入到 main.c 中了。

cc main.c -o program

这种方式就类似我们前面提到的方法 1 —— 将函数定义放在 main() 函数之前。

对于小项目来说,这种方式够用了。然而对于比较大的项目,这种方式有一个显著的缺点 —— 你会发现这种方式其实还是相当于把所有代码写入到了一个文件中。对于代码量大的项目,编译一个这样的文件可能相当耗时。并且你一旦对项目文件的任何部分做了改动,都要重新编译整个项目。显然这种方式不适合大型项目。

参考我们之前的做法,我们能不能只把函数声明的部分提取出来,然后把它们 includemain.c 文件中?这样 main.c 文件就只包含其他文件的声明部分,而不是全部代码。这样我们在编译的时候,就可以各个文件分别编译。如果其中某个文件发生了变动,我们只需要重新编译这个变动的文件,再重新链接即可。而链接的过程是比较快的。相比重新编译整个项目,显然这是更优的选择。

对于我们的这个例子,我们只需再创建一个 foo.h 文件,并将 foo.c 文件中所有函数的声明提取出来放入其中,这样我们只需在 main.c 文件中加入 #include "foo.h" 命令,就可以只将这些函数声明加入 main.c 文件,而不是全部代码。

我们把这种从源文件 foo.c 中提取函数声明组成的文件 foo.h 叫做头文件(header)。

// main.c
#include "foo.h"int main() {foo1();foo2();
}
// foo.h
int foo1();
int foo2();
// foo.c
#include "foo.h"int foo1() {...
}int foo2() {...
}

在这里你看到源文件 foo.c 也包含了其自身的头文件 foo.h,是因为在实际应用中头文件往往不止包括函数声明,也包括结构体声明、常量定义等源文件也必须用到的信息。因此在实际应用中源文件也常常包括其自身的头文件。

此时我们让编译器对 main.c 文件进行预处理:

cc -E main.c

就会看到 main.c 文件只包含了 foo.c 文件中函数的声明:

int foo1();
int foo2();int main() {foo1();foo2();
}

让编译器对 foo.c 文件进行预处理:

cc -E main.c

可以看到 foo.c 文件也包含了自己的函数声明:

int foo1();
int foo2();int foo1() {...
}int foo2() {...
}

如果我们想要构建项目,需要分别编译 main.cfoo.c,最后再进行链接:

# 编译
cc -c main.c
cc -c foo.c# 链接
cc main.o foo.o -o program

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.liansuoyi.cn/news/54680547.html

如若内容造成侵权/违法违规/事实不符,请联系连锁易网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

MySQL 是怎么执行 SQL 语句的?

Pthon 是怎么和 MySQL 交互的 假设你现在要用 Python 开发一个书籍管理系统,让管理员能够对 MySQL 数据库中的书籍信息进行增删改查,那么你会怎么做呢?其实很简单,使用 Web 框架编写一个服务,提供好相应的 API,当请求到来时,根据请求类型和参数拼接 SQL 语句,然后交给 …

springboot自动配置

首先,你在pom文件里引入的很多第三方jar里都有一个文件 META-INF/spring.factories,这个文件里的内容和关系到能否自动配置, 那有的jar为啥没有,是不需要SpringBoot来自动配置吗,这个我们后面再说。先来看一下redisson starter的/ META-INF/spring.factories是怎么写的, …

CCLINK转Profinet网关功能介绍及配置方法

CC-LINK转Profinet网关(XD-PNCR20)是一种用于连接CC-LINK和Profinet两种不同通信协议的设备,其功能是实现两种不同协议之间的数据通信和转换。通过这个网关设备,用户可以方便地将CC-LINK设备和Profinet设备进行连接,实现数据的传输和共享。CC-LINK转Profinet网关(XD-PNCR…

【IT老齐028】Redis Cluster集群模式

【IT老齐028】Redis Cluster集群模式Cluster模式是Redis3.0开始推出 采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接 官方要求:至少6个节点才可以保证高可用,即3主3从;扩展性强更好做到高可用 各个节点会互相通信,采用gossip协议交换节点元…

提高级字符串

哈希和哈希表 前缀函数&&KMP 前缀函数 定义:\(nxt[i]\) KMP 例题 1.无线传输(luogu4391/ybt1467) 求一个字符串的最短周期 结论: \[ans=n-nxt[n] \]证明:两条白线是最长的相等的前缀和后缀 可得 \(1=2,2=3,3=4,4=5......\) 所以周期的长度就是1的长度 也就是\(n-nxt…

计讯物联智慧合杆在智慧城市中的应用

随着智慧城市数字化的建设与发展,5G智慧合杆不仅成为智慧城市感知网络体系的重要载体,也是发展智慧城市IoT的重要基础。在今年两会中,全国政协委员提出,探索“智慧灯杆+5G基站”“多杆合一”试点示范,实现社会资源共享。显而易见,智慧合杆的发展在政策的引导和市场的主导…

如何保护非结构化数据,防止敏感信息泄露?

在21世纪的数据驱动时代,企业的数据安全问题显得尤为重要,尤其是非结构化数据的保护。在这篇文章中,我们将讨论如何保护非结构化数据,防止敏感信息泄露。非结构化数据包括企业的设计图纸、研发成果、内部决议、财务报告、商业合同、客户信息等,这些数据往往分散在不同的终…

UVM - 6 (Transaction Modeling)

内容uvm_sequence_item是transaction的基类 可以使用uvm_sequence_item和uvm_transaction对数据进行建模什么是事务(transaction)?总线协议可以认为是一个事务UVM数据流testcase一般不产生数据,通常进行config sequencer会产生激励给到driver,传递的就是transaction driver和d…

dolphinscheduler无法启动工作流

dolphinscheduler版本:3.2.0情景复现:当操作dolphinscheduler web ui任意删除按钮功能时。 dolphinscheduler工作流运行按钮。master节点报错日志如下: View Code 根据错误栈信息,可以锁定源码TriggerRelationServiceImpl第50行查询语句导致selectOne报错,sql查询信息如下…

django安装项目 app流程

#django版本问题 1..x 2.x 3.x(直接忽略) 1.x 和2.x本身差距也不大 我们讲解主要以1.x为例 会讲解2.x区别 公司之前用的1.8 慢慢过渡到了1.11版本 有一些项目用的2.0 #django安装pip3 install django==1.11.11 在Python中 设置里找jango 不要直接安装 需要选择1.11…

从SpringBoot到DotNet_3.完成购物模块

一、从购物车模型完成购物车 (一)购物车模型设计 ​ 使用 Guid 作为购物车的主键,让 EFCore 自己管理外部引用,使用 ICollection保存商品的信息,这里将商品抽象成LineItem与其他模块进行解耦。 namespace FakeXiecheng.Models; public class ShoppingCart {[Key]public Gu…

【C#】NPOI 操作 word 文档插入图片

使用NPOI操作,word文档必须为docx格式。 NPOI对文档插入图片的操作实际分为:向段落中插入图片、向表格中插入图片。 添加NPOI包实际操作 文档模板 insertpic为插入图片需要的自定义定位标签//添加引用 using NPOI.XWPF.UserModel;namespace ConsoleApp_Archive {internal cla…

text

zedboard基于vivado的试验 试验①:点亮一个led灯,使用一个按钮做复位。 project_2.v module project_2(input clk, //时钟输入input rst, //复位,高电平有效output reg led //led输出);always@(posedge clk or posedge rst)beginif(rst) //复位按钮按下就给led一个低电平led&…

读算法的陷阱:超级平台、算法垄断与场景欺骗笔记09_社会效应

读算法的陷阱:超级平台、算法垄断与场景欺骗笔记09_社会效应1. 价格歧视的社会福利效应 1.1. 价格歧视很难被简单归类为好或坏,它的社会经济效应是个多面体 1.1.1. 日益普及的自动化电子交易创造了一个透明的市场环境,资源分配效率提升、价廉物美产…

高频DC/DC变换器与充电桩应用

方案概述基于GD32F303的高频DC/DC变换器解决方案 车载充电器(OBC)是电动汽车和混合动力汽车的重要组成部分(HEV),通常由一个AC/DC(功率因数校正电路)和一个隔离式DC/DC转换器组成。随着电池容量增加带来的车内空间和散热需求,对OBC提出了更高的功率密度和转换效率要求。宽带…

滴水逆向笔记系列-1.进制-2.数据宽度_逻辑运算-3.通用寄存器_内存读写

第一课 进制 这节课讲进制计算的核心就是查表例:3+5,就是从上表的3开始往后数五个数,10例:46则是看作6+6+6+6,6+6由上表可知为14,14再往后数12个数得出为46=30八进制复杂计算(文字比较难说明,但是大致还是和我们十进制的计算方式一样,只是九九乘法表换成上面三张表作业…

每日反思之学会结对编程

长时间独立开发让人容易进入心流,也容易走向习惯性思维的误区。 想起过去创业的时候,总是一个人写代码,自测,merge代码,分布上线,反馈之后再调整。整个过程全靠自己去思考并完成,在一些不复杂的情形下算是最简最快的方案。但当自己融入到一个大的团队和项目中,合作分工…

Hadoop安装与使用

Hadoop安装与使用 创建Hadoop用户创建hadoop用户可以使不同用户之间有明确的权限区别,同时也可以使针对Hadoop用户的配置操作不影响其他用户的使用 创建用户命令如下: sudo useradd -m hadoop -s /bin/bash 设置用户密码,方便记忆可以设为hadoop sudo passwd hadoop 然后为ha…

搭建TS环境遇到的问题

1、npm安装TS后,查看TS版本,执行tsc -v报错信息为一个for循环 解决方法:原因是nodejs的版本太低,升级nodejs的版本即可解决2、vscode中执行:终端->运行任务->typescript->tsc:监视-tsconfig.json, 报错:typeScript 解决报错tsc : 无法加载文件 D:\node\nodejs\…
推荐文章