02 如果没有头文件,C/C++ 的世界将会怎样 您所在的位置:网站首页 scanf的头文件 02 如果没有头文件,C/C++ 的世界将会怎样

02 如果没有头文件,C/C++ 的世界将会怎样

2023-03-13 08:59| 来源: 网络整理| 查看: 265

0、前言

C/C++ 关于头文件的基本用法,大家应该都很清楚了。比如在 xx.h 中声明一个函数,声明一个结构体类型,定义一个宏等,然后在 xx.c 定义头文件中的函数、使用头文件中定义的结构体类型、宏等。可是,各位有没想过以下两个问题:

可以不要头文件吗?头文件在程序的编译、链接过程中扮演了什么样的角色呢?1、几个简单的例子

例 1:

// xx1.c void f1(){}; void main() { f1(); }

在 xx1.c 文件中,定义了一个函数 f1,然后在主函数 main 中调用了它,这个程序没有问题。但是,如果你一不小心写成了下面这种形式:

例 2:

// xx1.c void main() { f1(); }; void f1() {}

那么,编译器在第一次看到 f1 的时候,它不会报错,它认为在该源文件的后面会找到这个函数的详细信息,所以它记录下这个函数。但是编译器必须得知道这个函数的返回类型,它会假设 f1 返回 int。等到编译器看到实际函数时,返回的却是 void,这时就会报出“函数类型冲突”的错误。此时,解决方法除了把 f1 定义放在 main 之前外,还可以写成下述形式,

例 3:

// xx1.c void f1(); // 函数声明 void main() { f1(); }; void f1() { }

现在,假如有个项目工程,里面涉及的函数个数成千上万,你总不能把所有函数都定义在一个文件中吧,于是,你给分成了两个 C 文件 xx1.c 和 xx2.c

例 4:

// xx1.c void f1() { f2(); } // xx2.c void f2() { }

这个例子中,f1 要调用 f2,编译器扫到这里的时候需要知道 f2 的函数原型或者定义,会遇到例3 同样的问题。

很显然,如果没有 C 文件,大型 C 语言项目开发将会变得寸步难行。于是,我们引入了头文件。

3、头文件带来的好处

对于每个 xx.c 文件,我们都写一个与之对应的 xx.h 文件,头文件用于函数的声明,c 文件中实现函数。如果某个 xx1.c 文件中要调用 xx2.c 中的某个函数,只需要包含 xx2.h 就可以了。

例 5:

// xx1.h void f1(); // xx1.c include "xx2.h" void f1() { f2(); } // xx2.h void f2(); // xx2.c void f2() { }

编译器是怎么处理源文件和头文件的呢?

编译器在编译时是以 C 文件为单位进行的,也就是说如果你的项目中一个 C 文件都没有,那么你的项目将无法编译,每个 C 文件编译完生成目标文件 .o, 链接器以目标文件为单位,再将一个或多个目标文件进行函数与变量的重定位,生成最终的可执行文件。

编译器处理 C 文件时,首先读到的是 include 头文件包含语句,然后,将那个头文件里的内容原封不动地复制到当前 C 文件。

xx.h 声明的函数一定要在 xx.c 中定义吗?

答案是否定的。假如某个 C 文件里调用函数 f (f 声明在 xx.h 中)那么该 C 文件 只要通过 include “xx.h” 找到相应的函数原型声明就可以了,至于这个函数是在 xx.c 还是在 yy.c 里定义的根本不重要。链接器在链接过程中会根据符号寻找相应的代码段地址的。

4、总结头文件中声明函数,是为了告诉需要调用这个函数的 C 文件该函数的原型;一般 xx. c 会对应一个 xx.h,这样代码组织结构清晰,同时容易检查函数声明的类型和定义时的返回类型是否一致;头文件的存在便于实现对函数的管理。比较坑的一点:如果把某个函数放在头文件中实现(且该函数对编译选项有特定的需求),那么所有包含该头文件的 c 文件在编译的时候,都需要保证一样的编译参数。还有最重要的一点,头文件的存在为了闭源发布动态库(dll 或者 so)的需要

对了,C 文件中的 include 语句可以放在任意行,只要保证后面用到的符号在前面即可。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有