C语言头文件

函数库在我们的计算机中对应着这样三种文件:

  1. 头文件(Header Files),后缀为 .h,只写了库函数和变量的声明
  2. 静态库文件(Static Library ),在Windows下后缀为 .lib,在Linux下后缀为 .a。这个文件中真正包含了某些库函数的实现(即目标代码)。但是这个文件不是C语言的代码,而是二进制码
  3. 动态链接库文件(Dynamic Linked Library),在Windows下后缀为 .dll,在Linux下后缀为 .so。这个文件也包含了某些库函数的实现,同样是二进制码。

C语言程序的完整编译和运行过程
1.预处理阶段。预处理器从头至尾扫描文件,寻找#开头的预处理指令,进行文本替换和插入。这时,我们#include的头文件(函数和变量声明)将被完全插入到include所在位置。
2.编译阶段,编译器对上阶段预处理好的程序进行分析和合成,生成目标文件(机器码),后缀是 .obj,在win7下是.o文件。

需要指出的两点:
1. 静态库文件 .lib,在以往的Windows环境中静态库文件后缀为.lib,但在win7下的后缀其实不是 .lib,而是和Linux下同样的 .a。而这个 .a文件被 视为是.o的归档,也就是说.a和.o本质相同,都是二进制代码,只是.a相当于放了多个.o中的内容。因此,静态库文件.a完全可以当成目标文件.o来用,或者说它也是另一种形式的目标文件
2. 在编译的过程中对于调用的函数并不需要具体的实现,只要知道函数的原型即可,编译器在函数调用的地方生成一个引用符号,这个引用符号会被下个阶段(链接)识别。因此,调用只声明而未定义的函数在这一阶段是不会报错的。

3.链接阶段,链接器将各个目标文件中的各段代码进行绝对地址定位,生成跟特定平台相关的可执行文件(executable file)。这个可执行文件就是真正的可以被加载到内存中,然后能被底层硬件所执行的文件。比如Windows下的.exe文件。

同样说明两点:
一:上个阶段编译器在函数调用的地方生成了引用符号,在链接阶段必须去找到引用符号的实现(二进制代码),如果函数定义在C文件中,链接器就会去找到那个对应的目标文件.o,如果是静态库函数,则在 .a中,对于这两者链接器都会将它们加入到 .exe文件中。如果找不到对应的函数定义,会报错,如果找到多个而导致无法区分,也会报错。
二:对于标准库文件只需要引用头文件,链接的时候编译器自动查找对应的.a。而对于自己的库,不但在要源码中包含头文件,在链接时还要指明库文件。

4.运行阶段,程序真正运行过程中,还可以调用动态链接库.dll,这个dll文件,我们可以认为它是exe文件的半成品,它没有main函数,因此不能被直接执行,但它可以被exe“动态”地引用和卸载,它和静态库的最大不同在于,静态库.a中内容最后都要被加入到exe中,而dll是独立于exe的,它只是被调用,然后又被释放,dll中的内容并没有被加入exe。这样带来的好处是,内存中只有DLL的一份复制品,节约了内存资源。Dll的调用分为静态调用和动态调用,静态调用需要相应的导入库.a文件来提供DLL 导出函数的符号名及序号,动态调用则需要用LoadLibrary和GetProcAddress语句去寻找函数的入口。

.h文件给出了函数的声明,在预处理阶段被加入程序代码中,至于函数的定义,如果放在.a文件中,则在链接阶段就把.a文件加入到.exe文件中,如果放在.dll文件中,则在程序运行阶段才会由应用程序去调用dll中的函数
用这样的方式来实现库函数,带来的意义是:
1. 函数的实现过程被隐藏为二进制码的形式,而不是源码,既保护了函数库编写者的版权,又提升了可移植性
2. 函数的声明以头文件形式被提供给用户,用户通过头文件中的声明了解库函数的功能和使用方法

自己写出一个函数库:
在集成开发环境下,我们新建一个工程,平时我们总是新建控制台工
程,其实我们还可以新建静态库工程或是动态链接库工程,这样编译出来的就是.a和.dll。如果是自己亲自写编译命令,就要先把
C或CPP文件编译成.o目标文件,再用ar 命令(linux)把.o文件打包归档为.a文件,或是用gcc-shared命令(gcc 编译命令)将其创建为.dll文件

既然人人都可以编写库函数,于是就出现了各种公司和编译器提供的各种不同的库函数,这也造成了极大的混乱。后来标准化组织ANSI制定了C语言的标准,同时也制定了函数库的标准,就成为 标准库 函数。后来这个ANSI C标准上升为ISO标准,但是内容并没有任何变化。C++也是一样,ISO为C++制定了标准之后,所有的C++编译器也就按照这个标准提供库函数。
需要强调的是,标准库并非C语言的一部分,它只是C语言的一个辅助工具


我们必须再次认识一下头文件(header file),头文件中包含了变量和函数的声明,而不包含它们的定义,这样做带来的意义是:
1.使程序编写变得简单:对于多个C文件都要调用的全局实体(可能是函数,变量,结构,类等等),如果在每个文件中声明一次,显得非常麻烦。我们把对于这些实体的声明写进头文件中,再用include指令把头文件加入到C文件中,就会变得简单。而且修改起来变得更加容易,因为只需要修改头文件中的内容。如果把对于那些实体的定义也写头文件,那么当多个C文件include那个头文件,将会多次定义同一个实体,链接时就会出错。

2.头文件承担了一个重大责任,就是作为函数库的目录与接口,和用户进行交流。头文件中包含了对于函数的声明,这就意味着,通过查看头文件,我们就可以知道库中有哪些函数,以及这些函数的原型,也就知道了这些函数需要什么参数,返回什么值,如何去调用它。这就好比图书馆给用户一份目录清单,告诉你馆里有哪些书,以及对那些书进行简单的介绍。
3.保护了函数库编写者的版权,头文件中没有函数的具体实现,这意味着我们只能通过看头文件来知晓库函数的使用方法,而不能了解它的实现方法。真正的函数实现作为二进制码被隐藏于.a和.dll 文件中,只能被机器识别

在C语言的标准中,规定头文件后缀为 .h ,例如包含了scanf函数的

在C语言的标准中,规定头文件后缀为 .h ,例如包含了scanf函数的<stdio.h>。而在C++的标准中头文件
则不采取加后缀的形式,如<iostream>。注意,C++作为C的超集,是包含C的,我们完全可以在C++程序中包含一个
<stdio.h>,但是这就造成了程序设计风格上的混乱(所以不建议这么写)。为此,C++又提供了<cname>形式的标
准头文件,其内容与ISO标准C包含的name.h头文件相同,但容纳了C++扩展的功能。在 <cname>形式标准的头文件
中,与宏相关的名称在全局作用域中定义,其他名称在std命名空间中声明。

下面给出C89标准中标准库的15个头文件:
<assert.h> 验证程序断言,用于诊断
<ctype.h> 字符类型和字符类测试
<errno.h> 说明出错码
<float.h> 浮点运算有关的常量
<limits.h> 实现和定义时的限制
<locale.h> 声明了C语言本地化函数 
<math.h> 说明了数学函数和宏
<setjmp.h> 非局部跳转
<signal.h> 信号处理部分, 规定了了程序执行时如何处理不同的信号。 
<stdarg.h> 可变参数表,提供了依次处理含有未知数目和类型的函数变元表的机制
<stddef.h> 标准定义,定义了一些标准宏以及类型 
<stdio.h> 标准I/O库,定义了用于输入和输出的函数、类型和宏。
<stdlib.h> 实用程序库函数
<string.h> 字符串操作处理函数
<time.h>  日期和时间的类型和函数

----------

95年对C89的修订版新增了三个
<iso646.h> 替代关系操作符宏 
<wchar.h> 宽字符支持
<wctype.h> 宽字符分类和映射支持

----------

C99版本又新增了六个
<complex.h> 支持复数算术运算
<fenv.h> 浮点环境
<inttypes.h> 整型格式转换
<stdbool.h> 布尔类型和值
<stdint.h> 整型
<tgmath.h> 通用类型数学宏

----------

C++ 标准库一共包含18 个 C的标准库头文件:
<cassert> 用于在程序运行时执行断言    <cctype> 字符处理
<cerrno> 错误码                              <cfloat> 用于测试浮点类型属性 
<ciso646> ISO646变体字符集            <climits> 测试整数类型属性
<clocale> 本地化函数                        <cmath> 数学函数
<csetjmp> 执行非内部的goto语句       <csignal> 信号
<cstdarg> 访问参数数量变化的函数     <cstddef> 用于定义实用的类型和宏
<cstdio> 输入/输出                          <cstdlib> 杂项函数及内存分配
<cstring> 字符串                             <ctime> 时间
<cwchar> 宽字符处理及输入/输出        <cwctype> 宽字符分类

----------

和33 个C++ 头文件:
<algorithm> STL通用算法   <bitset> STL位集容器               <complex> 复数类
<deque> STL双端队列容器    <exception> 异常处理类            <fstream> 文件流
<functional> STL函数对象    <iomanip> 参数化输入/输出        <ios> 基本输入/输出支持
<iosfwd> 输入/输出前置声明  <iostream> 数据流输入/输出       <istream> 基本输入流
<iterator> 遍历序列的类       <limits> 各种数据类型最值常量     <list> STL线性列表容器
<locale> 国际化支持            <map> STL映射容器                   <memory> 专用内存分配器
<new> 基本内存分配和释放   <numeric> 通用的数字操作            <ostream> 基本输出流
<queue> STL 队列容器        <set> STL 集合容器                     <sstream> 基于字符串的流
<stack> STL 堆栈容器         <stdexcept> 标准异常                  <streambuf> iostream 的缓冲区类
<string>字符串类                <strstream>非内存字符序列的流类  <typeinfo> 运行时类型标识
<utility> STL 通用模板类      <valarray>支持值数组的类和模版类  <vector> STL 动态数组容器
以及附加的3个头文件(非必须)
<hash_map> <hash_set> <slist>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值