C头文件与库文件的区别

头文件一般而言,是申明和定义。
库文件是已经编译好的二进制代码。这个二进制代码可以是动态的,如 .so 也可以是静态的,如 .a ,如果是动态的,则你最后生成的程序文件在运行时,需要这个动态库的支持,如果是静态的,则你最后生成的客执行程序文件运行时可以脱离这个库文件而独立运行。
头文件以.h结尾,可以用文本编辑器查看内容。是ASCII的。
而库文件以.a(静态库)或.so(动态库)结尾,是二进制的。

1.从几个问题谈起

问题一:undefined reference to 'xxx'.

问题二:/usr/bin/ld:cannot find -lxxx.

问题三:xxx.h:No such file or directory.

首先,这几个问题都不是编译错误,是链接错误,也就是如果出现的是这几个错误,说明你的源程序本身没有问题,是你的编译选项用的不对或者缺少相关的库文件或者头文件。前两个问题是找不到库文件的问题,后一个问题是找不到头文件的问题。下面详细说一下头文件和库文件相关的问题。

2.库文件相关(-l选项和-L选项)

编译完成之后就进入链接阶段,这里就涉及到函数库,比如通常的用的printf函数,我们仅仅在程序开始包含进了“stdio.h”,这个里面也只有该函数的声明,而没有定义函数的实现,那么,printf函数的实现在哪里呢?答案是在函数库中,链接时,gcc会链接到具体的函数库中,在那里可以找到printf函数的实现。

-l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so去掉就是库名了。

好了现在我们知道怎么得到库名了,比如我们自已要用到一个第三方提供的库名字叫libtest.so,那么我们只要把libtest.so拷贝到/usr/lib里,编译时加上-ltest参数,我们就能用上libtest.so库了(当然要用libtest.so库里的函数,我们还需要与libtest.so配套的头文件)。

放在/lib和/usr/lib和/usr/local/lib里的库可直接用-l参数就能链接了,但如果库文件没放在这三个目录里,而是放在其他目录里,这时我们只用-l参数的话,链接还是会出错,出错信息大概是:“/usr/bin/ld:cannot find -lxxx”,也就是链接程序ld在那3个目录里找不到libxxx.so,这时另外一个参数-L就派上用场了,比如常用的X11的库,它放在/usr/X11R6/lib目录下,我们编译时就要用-L/usr/X11R6/lib -lX11参数,-L参数跟着的是库文件所在的目录名。再比如我们把libtest.so放在/aaa/bbb/ccc目录下,那链接参数就是-L/aaa/bbb/ccc -ltest。

3.头文件相关(-I选项)

-I参数是用来指定头文件目录,/usr/include目录一般是不用指定的,gcc知道去那里找,但是如果头文件不在/usr/include里我们就要用-I参数指定了,比如头文件放在/myinclude目录里,那编译命令行就要加上-I/myinclude参数了,如果不加你会得到一个"xxxx.h: No such file or directory"的错误。-I参数可以用相对路径,比如头文件在当前目录,可以用-I.来指定。

 

手动来写这些编译选项参数一般比较麻烦,而且容易出错,比如,我程序中用到了glib库中的函数,由于glib库一般不是系统自带的库,我是手动编译源码安装的,默认安装路径是/usr/local/lib,头文件在/usr/local/include/glib-2.0下面。我编译我自己的代码时,如果直接编译,不加任何编译选项,会提示"glib.h: No such file or directory"错误,这里有两个解决方法。

方法一:第一安装的时候手动指定安装路径,安装到/usr下面,默认安装是直接”./configure”,  “make”和”make install”三条命令,如果要手动指定安装路径就用”./configure --prefix=/usr”,”make”和”make install”.

方法二:编译的时候添加编译选项。我这里没有直接用-l和-I选项,我利用了一个工具pkg-config。具体的编译命令是:“gcc`pkg-config --cflags --libs glib-2.0 ` test.c”。

 pkg-config会自动的生成指定库的库文件和头文件路径,pkg-config的用法就是“ pkg-config --cflags --libs pkgName”, 其中pagName是包名,如果不知道具体的包名是什么,可以通过命令“pkg-config --list-all”查询,该命令会列出系统支持的所有开发包。

转自 http://blog.chinaunix.net/uid-27575921-id-4078661.html

### 头文件库文件的作用 在 C 语言中,**头文件**(.h)**库文件**(.a 或 .so)分别承担不同的功能。头文件主要包含函数声明、宏定义、结构体定义等内容,用于在编译阶段告知编译器函数的使用方式参数类型[^2]。例如,`#include <stdio.h>` 引入了标准输入输出库的声明,使得 `printf()` 函数可以被正确调用[^1]。头文件通过 `#include` 指令被包含在源文件中,多个头文件需要多个 `#include` 命令,而同一个头文件多次引入的效果等同于一次引入,这是由于头文件内部使用了防止重复引入的机制,例如条件编译指令 `#ifndef ... #define ... #endif`[^3]。 头文件不同,**库文件**(Library)是编译后的二进制文件,包含实际的函数实现代码。C 语言中的库文件分为静态库(.a)动态库(.so),它们在链接阶段被链接器使用,将程序中调用的函数库中的实现代码绑定在一起,从而生成可执行文件。例如,`printf()` 函数的具体实现就包含在标准 C 库(libc)中,链接器会自动将其程序绑定,而无需开发者手动实现。 ### 头文件库文件的协作 头文件库文件通常协同工作,头文件提供接口定义,而库文件提供实际的实现。开发者在编写程序时,首先通过 `#include` 指令引入头文件,以声明所需的函数变量类型;然后在编译链接阶段,编译器链接器会使用对应的库文件来解析这些函数的实际代码。例如,在使用数学函数 `sqrt()` 时,必须在源文件中包含 `#include <math.h>`,并在编译时链接数学库 `-lm`,否则链接器会报告未定义的引用错误。 ### 文件类型的区别 头文件通常是纯文本文件,可以使用任何文本编辑器打开并查看其内容,它并不包含函数的具体实现,仅用于编译阶段的声明检查。库文件则是二进制文件,无法直接阅读,它包含多个目标文件(.o)的集合,经过编译、优化打包处理后供链接器使用。 ### 文件包含方式 在使用头文件时,`#include` 指令可以采用两种格式:系统头文件使用 `<header.h>` 格式,例如 `#include <stdio.h>`,而用户自定义头文件则使用 `"header.h"` 格式,例如 `#include "myheader.h"`。库文件则不会直接在源代码中包含,而是通过编译器选项(如 `-l` 参数)指定链接的库名称。例如,在 GCC 编译命令中使用 `-lm` 表示链接数学库。 ### 代码示例 以下是一个使用头文件库文件的简单示例: ```c // square.h #ifndef SQUARE_H #define SQUARE_H double calculateSquareArea(double side); #endif // SQUARE_H ``` ```c // square.c #include "square.h" double calculateSquareArea(double side) { return side * side; } ``` ```c // main.c #include <stdio.h> #include "square.h" int main() { double area = calculateSquareArea(5.0); printf("Square area: %f\n", area); return 0; } ``` 在编译过程中,首先需要将 `square.c` 编译为目标文件,然后将其打包为静态库或动态库,并在链接 `main.c` 时使用该库: ```bash gcc -c square.c ar rcs libsquare.a square.o gcc main.c -L. -lsquare -o main ``` ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值