函数的声明和定义,编译过程
声明:
让编译器知道函数的名字,参数,返回类型。可以不存在。一般需要在函数使用之前,一定要先声明,再使用。
定义:
函数的定义就是函数的具体实现。
编译过程:
c语言的编译过程是:
预处理(需要头文件)---->编译---->汇编----->链接(需要库文件)
事实上头文件和库文件没有直接联系,头文件的作用就是告诉编译器,函数如何调用和返回,具体调用在链接阶段完成。
#include
两种include
#include<xxxx.h> //编译器从编译系统子目录中去查找所需要头文件
#include"xxxx.h" //编译器先在用户当前目录查找,再按标准方式查找
#include"C:\temp\file1.h" //指定查找目录
当然可以直接在gcc时指定include的路径。
头文件和库文件的区别:
头文件:
一般放函数的声明;头文件是文本文件,可以供阅读。在源程序中,如果需要用到某个函数,就需要先声明函数,此时可以#include包含该声明的头文件。
库文件:
放函数的实现(定义)。库文件是二进制文件,不可直接阅读。库文件是将一系列源文件进行编译打包,形成的二进制文件包。其中封装着函数接口,在编程中可以由其他源文件进行调用。
头文件
头文件在编译时使用
头文件是函数或定义的声明。一般不包含非静态函数的实现。在调用库函数时,要#include接口函数所在的头文件。
库文件通过头文件向外导出接口,用户通过头文件找到库文件中的函数实现的代码,从而把这段代码链接到用户程序中。
我们可以把全部的#define,函数声明等共用的东西,全部放在头文件中。
库文件
库文件在链接时使用
C语言中的库文件分为.so文件和.a文件。
(1).so文件
.so文件为动态链接库,在程序执行时动态加载,gcc在链接时优先使用动态链接库。
进行代码连接时,连接器将你的函数调用连接到相应的DLL的对应函数(动态库),运行时才会链接。 编译时只是产生一些DLL内的代码的导入表,真正运行时才调用DLL中的代码。
(2).a文件
.a文件为静态链接库,只有在动态链接库不存在时,才使用静态链接库。可以使用 -static强制使用。 gcc xxx.c -static -lxxx
编译时连接器将函数的实现代码(静态库)连接到你的程序。相当于在编译时就将函数的实现嵌入到了原码中。
举例说明:创建静态库文件
(1)源程序与头文件
头文件和源程序在两个不同文件夹xxx/test和*xxx/testlib下
xxx/test中有源程序
源程序test.c
#include<stdio.h>
#include<test.h>
int main(){
char *s="aaaaaa";
testfun(s);
return 0;
}
xxx/testlib中有头文件和函数实现
头文件:test.h
#define STEST 12
void testfun(char *);
库函数testfun.c
#include<stdio.h>
#include "test.h" //优先搜索当前目录下文件,找不到再去指定和默认目录下找
void testfun(char *s){
printf("%s,num=%d",s,STEST);
}
头文件对函数和常量进行声明,库函数实现函数。源程序调用函数。
(2)创建.o文件
gcc -c testfun.c ---->testfun.o
(3)归档
将多个.o文件归档到.a文件
ar crv libtest.a testfun.o
库文件名 包含的.o文件
(4)编译源程序
gcc test.c -I xxx/lib/ -L xxx/lib/ -ltest
指定头文件目录 指定库文件搜索路径 指定要链接的库文件
举例说明:创建动态库
大概步骤相同。
区别有几个:
(1)生成.o文件时:
gcc -fpic -c testfun.c //比静态.o多一个fpic
(2)生成.so库文件
gcc -shared -o libtest.so testfun.c
以上两步可以合并:
gcc -fpic -shared test.c -o libtest.so
(3)编译
编译时和静态库没有差别
(4)运行
运行时报错:
这是因为程序在运行时没有找到动态链接库造成的。
静态库是在编译时链接,而动态库是在运行时链接,两者不同。
运行时链接,需要动态库在系统目录下才行。
解决方法是将libtest.so文件复制到/lib目录下。
sudo cp /home/billy/demolib/libtest.so /lib
此时运行正常。