Linux C C语言库的创建和调用
C语言库的创建和调用
简介:
假如,你有一个庞大的工程,代码量达到数百兆甚至是数G,你经常会遇到好多重复或常用的地方。每次使用到这些地方时如果都重新写一份基本相同的代码,这当然可以,不过这样会大大地降低工作效率,而且影响代码的可读性。更不好的是日后的修改工作会使你变得非常的繁琐,这样很不利于后期的维护。如果把这些相同的功能代码分别以模块的形式存放起来,把他们编译成库,使用时直接调用他们的库,这样直接使用起来非常的方便,更有利于代码的维护和升级。
库的概念:
库是由源代码编译出来的,是对一组源文件编译出来的中间文件。使用库可以做到不开放源代码,同时令其他单元可以调用到的效果,实现良好的接口封装。
其实,现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。比如我们常使用的printf函数,就是c标准库提供的函数。我们在使用时只需要包含相应的头文件就可以使用(非静态编译还要有相应的库文件)。而不用关心printf函数具体是如何实现的,这样就大大提高了程序员编写代码的效率。
库的分类:
库大体上可以分为两类:静态库和动态库。在windows中静态库是以 .lib 为后缀的文件,共享库是以 .dll 为后缀的文件。在linux中静态库是以 .a 为后缀的文件,动态库是以 .so为后缀的文件。
生成两种库的方式:
生成静态库:
首先将源文件编译成目标文件: gcc -c a.c b.c
然后将目标文件链接生成静态库: ar -rc libstatic.a a.o b.o
生成动态库:
首先将原文件编译成目标文件: gcc -fPIC -c a.c b.c
然后将目标文件链接生成动态库 gcc -fPIC --shared -o libshared.so a.o b.o
当然也可以一步生成: gcc -fPIC --shared -o libshared.so a.c b.c
库的链接方法
静态库的链接方法:
gcc -o static main.c -L./ -lstatic -static
动态库的链接方法:
gcc -o shared main.c -L./ -lshared
静态库和动态库各有各的特点,总的来说:静态库利用空间效率换取时间效率;动态库则利用时间效率换取空间效率。
静态库和动态库有个地方需要注意的:当程序与静态库连接时,库中文件所含的所有程序将被程序的使用函数复制到最终代码文件中;当程序与动态库链接时可执行文件只包含它需要的函数的引用表,而不是所有的函数的代码,只有程序在执行时,才将需要的代码拷贝到内存中,明显的利用时间来换取空间。这是需要注意的是:一个程序编译好了后,当需要修改的刚好是库函数时,在接口不变的情况下,使用动态库的只需要将动态库重新编译一遍就行了,而使用静态的的程序则需要将静态库重新编译好后,同时将调用的程序也编译一遍。
使用动态库需要注意的事项:
使用静态库是在可执行程序链接是就已经加入代码中了,在物理上成为代码的一部分,因此使用静态库编译的程序运行时告知在哪里能找到库。而动态库是在程序娙时才加载到程序中的,运行时,机器需要找到库将代码补充完整。
使用动态库的方法和注意事项:如果程序连接时使用了动态库,就必须在程序运行时能够找到动态库的位置。Linux的可执行程序在执行的时候默认先搜索/lib和/usr/lib这两个目录,然后按照/etc/ld.so.conf里面的配置搜索绝对路径。同时Linux也提供了环境变量LD_LIBRARY_PATH (如:
export LD_LIBRARY_PATH=/home/Linux/exercise/library/file2)供用户选择使用,用户可以通过它来设定它查找出默认路径外的其他路径。不过,LD_LIBRARY_PATH的设定作用是全局的,过多的设定可能会影响其他程序的运行。有些系统的gcc支持-R或-rpath选项,多建议使用这种方法和/etc/ld.so.conf或在/etc/ld.so.conf.d目录下新建一个.conf文件然后添加动态库的搜索路径,尽量避免使用LD_LIBRARY_PATH。
需要注意的是:添加路径需要在终端运行命令:ldconfig
本博客小结下C语言如何产生库让C调用和让C++调用。




例:library目录下有file1、file2、file3和LIB三个目录,如下:
library --file1 file1.c hello.h hello.c
--file2 file2.c hi.c hi.h
--file3 file3.h file3.cpp
--LIB
在当前目录下生成库调用
”这是个C语言生成C语言库给C语言调用的简单例子“
[root@centos-64-min file1]# cat file1.h
void play1();
[root@centos-64-min file1]# cat file1.c
#include<stdio.h>
#include"file1.h"
int main(void)
{
play1();
return 0;
}
[root@centos-64-min file1]# cat play1.c
#include<stdio.h>
void play1(void)
{
printf("hello file1\n");
}
使用静态库:
编译生成目标文件 gcc -c play.c
链接生成静态库 ar -rc libstatic.a play1.o
使用静态库编译 gcc -o staticfile file1.c -L/home/Linux/exercise/library/file1 -lstatic
运行结果:
[root@centos-64-min file1]# ./staticfile
hello file1
使用动态库:
使用静态库是在可执行程序链接是就已经加入代码中了,在物理上成为代码的一部分,因此使用静态库编译的程序运行时告知在哪里能找到库。而动态库是在程序娙时才加载到程序中的,运行时,机器需要找到库将代码补充完整。
使用动态库的方法和注意事项:如果程序连接时使用了动态库,就必须在程序运行时能够找到动态库的位置。Linux的可执行程序在执行的时候默认先搜索/lib和/usr/lib这两个目录,然后按照/etc/ld.so.conf里面的配置搜索绝对路径。同时Linux也提供了环境变量LD_LIBRARY_PATH (如:
export LD_LIBRARY_PATH=/home/Linux/exercise/library/file2)供用户选择使用,用户可以通过它来设定它查找出默认路径外的其他路径。不过,LD_LIBRARY_PATH的设定作用是全局的,过多的设定可能会影响其他程序的运行。有些系统的gcc支持-R或-rpath选项,多建议使用这种方法和/etc/ld.so.conf或在/etc/ld.so.conf.d目录下新建一个.conf文件然后添加动态库的搜索路径,尽量避免使用LD_LIBRARY_PATH。
需要注意的是:添加路径需要在终端运行命令:ldconfig
[root@centos-64-min file2]# cat file2.c
#include<stdio.h>
#include“hi.h”
int main(void)
{
hi();
return 0;
}
[root@centos-64-min file2]# cat hi.h
#include“hi.c”
void hi();
[root@centos-64-min file2]# cat hi.c
#include<stdio.h>
void hi(void)
{
printf("hi file2\n");
}
编译生成目标文件: gcc -fPIC -c hi.c
链接生成动态库: gcc -fPIC -shared -o libshared1.so hi.o
在配置文件中添加搜索路径 或
在终端运行命令: ldconfig
[root@centos-64-min file1]# gcc -g -o sharedfile file2.c -L/home/Linux/exercise/library/file2 -lshared1
[root@centos-64-min file1]# ./sharedfile
hello file1
在file1调试的其他一些小问题:
问题一:
[root@centos-64-min file1]# gcc --shared play1.c -o libplay1.so
/usr/bin/ld: /tmp/ccX7Elnx.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/tmp/ccX7Elnx.o: could not read symbols: Bad value
collect2: ld returned 1 exit status
解决方法:
[root@centos-64-min file1]# gcc -fPIC --shared play1.c -o libplay1.so
问题二:
[root@centos-64-min file1]# gcc -o file1 file1.c libplay1.so
[root@centos-64-min file1]# ./file1
./file1: error while loading shared libraries: libplay1.so: cannot open shared object file: No such file or directory
出现这种情况主要是因为执行文件找不到 play1库,致使链接不到,在编译时指明库所在的位置就可以了。
解决方法:
[root@centos-64-min file1]# gcc -o file1 file1.c ./libplay1.so
[root@centos-64-min file1]# ./file1
hello file1
运行结果:
[root@centos-64-min file2]# gcc -o file2 file2.c
[root@centos-64-min file2]# ./file2
hi file2
把file1和file2生成的库存放到LIB中
[root@centos-64-min file2]# cp libfile2.so ../LIB/
[root@centos-64-min file1]# cp libplay1.so ../LIB/
[root@centos-64-min LIB]# ls
libfile2.so libplay1.so
在file3中编写c++文件调用LIB中库
[root@centos-64-min file3]# cat file3.cpp
#include<iostream.h>
int main(void)
{
play1();
hi();
return 0;
}
[root@centos-64-min file3]# cat file3.h
#ifdef __cplusplus
extern "C"
{
#endif
void hi();
void play1();
#ifdef __cplusplus
}
#endif
[root@centos-64-min file3]# g++ -o file3 file3.h file3.cpp ../LIB/libfile2.so ../LIB/libplay1.so
[root@centos-64-min file3]# ./file3
hello file1
hi file2
c++调用C的库
首先,先了解
extern “C”的理解:
extern “C”指令描述的是一种链接约定,其并不影响调用函数的定义。即使使用了该声明,对函数的检查和参数的转换仍然遵循C++的规则。
extern "C"的作用:
不同的语言链接性是不同的,这样也决定了他们编译后的链接符号的不同,例如,函数void fun(double a),C语言编译后是_func这样的符号,C链接器只要找到这样的符号就能够成功地链接,它默认假设参数类型信息是正确的。而C++会把它编译成 _fun_doublle 或 _xxxfunDxxx这样的符号,在C语言编辑器的符号基础上增加了类型信息。这也是C++可以有重载函数的原因。
介于此,对于用C编辑器编译出来的库,用C++直接链接势必会出现不能识别符号的问题,这时候就需要用 extern "C" 来告诉编译器要以C语言的方式编译和链接该封装函数。
上面file3文件里的例子就是c++调用C的一个示例。
__cplusplus宏的条件编译
为什么要加这个编译条件呢,因为extern "C"语法在C编译环境下是不允许的。这样这的写法防止当这个文件被用作C文件编译时,可以去掉C++结构,也就是说,防止test.cpp改为test.c,#include<iostream>改为#include<stdio.h>时,C编译器也能能够顺利的编译通过
这种技术也可能会用在由C头文件和C++头文件中,这样使用是为了建立起公共的C和C++文件,也就是保证当这个文件被用做C文件编译时,可以去掉C++结构,成功编译。
比如:将上面的file3.cpp更名为file3.c,将头文件改为stdio.h,将条件编译去掉,再用gcc编译也能正确地编译。而即使做了上面的修改,这样就能公共地为C和C++文件使用
有这个条件预编译的结果:
[root@centos-64-min file3]# gcc -o file3 file3.h file3.c ../LIB/libfile2.so ../LIB/libplay1.so
[root@centos-64-min file3]# ./file3
hello file1
hi file2
没有这个条件编译的结果:
[root@centos-64-min file3]# gcc -o file3 file3.h file3.c ../LIB/libfile2.so ../LIB/libplay1.so
file3.h:2: error: expected identifier or ‘(’ before string constant
In file included from file3.c:2:
file3.h:2: error: expected identifier or ‘(’ before string constant
Linux C C语言库的创建和调用的更多相关文章
- Windows下静态库、动态库的创建和调用过程
静态库和动态库的使用包括两个方面,1是使用已有的库(调用过程),2是编写一个库供别人使用(创建过程).这里不讲述过多的原理,只说明如何编写,以及不正确编写时会遇见的问题. //注:本文先从简单到复杂, ...
- QT共享库的创建与调用(初级)(附:UI界面不能被改变的其中一个原因)
背景: 最近在做的一个项目其中一部分既是实现PC与下位机的USB通信.windows平台下已经完成,现需移植到linux平台下. 在linux系统中,通过一段时间的工作,设备已被配置成hid类(后续再 ...
- Qt 共享库(动态链接库)和静态链接库的创建及调用
前言: 编译器 Qt Creator, 系统环境 win7 64 位 1.创建共享库: 新建文件或项目->选择 Library 和 c++ 库->选择共享库->下一步(工程名为 sh ...
- linux下的C语言开发 进程创建 延伸的几个例子
在Linux下面,创建进程是一件十分有意思的事情.我们都知道,进程是操作系统下面享有资源的基本单位.那么,在linux下面应该怎么创建进程呢?其实非常简单,一个fork函数就可以搞定了.但是,我们需要 ...
- linux静态与动态库创建及使用实例
一,gcc基础语法: 基本语法结构:(由以下四部分组成) gcc -o 可执行文件名 依赖文件集(*.c/*.o) 依赖库文件及其头文件集(由-I或-L与-l指明) gcc 依赖文件集(*.c/*.o ...
- LINUX下C语言编程调用函数、链接头文件以及库文件
LINUX下C语言编程经常需要链接其他函数,而其他函数一般都放在另外.c文件中,或者打包放在一个库文件里面,我需要在main函数中调用这些函数,主要有如下几种方法: 1.当需要调用函数的个数比较少时, ...
- Linux库的创建和使用
Linux库的概念 库是一种软件组建技术,里面封装了数据和函数,提供给用户程序调用.使用库能够使程序模块化,提高编译速度,实现代码重用,易于升级. Windows系统提供了大量静态链接库(.lib)和 ...
- linux下的静态库创建与查看,及如何查看某个可执行依赖于哪些动态库
linux下的静态库创建与查看,及如何查看某个可执行依赖于哪些动态库 创建静态库:ar -rcs test.a *.o查看静态库:ar -tv test.a解压静态库:ar -x test.a 查 ...
- MySQL进阶11--DDL数据库定义语言--库创建/修改/删除--表的创建/修改/删除/复制
/*进阶 11 DDL 数据库定义语言 库和表的管理 一:库的管理:创建/修改/删除 二:表的管理:创建/修改/删除 创建: CREATE DATABASE [IF NOT EXISTS] 库名; 修 ...
随机推荐
- JDBC批处理读取指定Excel中数据到Mysql关系型数据库
这个demo是有一个Excel中的数据,我需要读取其中的数据然后导入到关系型数据库中,但是为了向数据库中插入更多的数据,循环N次Excel中的结果. 关于JDBC的批处理还可以参考我总结的如下博文: ...
- /var/spool/postfix/maildrop小文件太多造成inode索引使用完解决
/var/spool/postfix/maildrop 小文件太多造成inode索引使用完解决办法 问题表现和检查: 1.运行df -i / 查看inode使用是否满: 2.查看/var/spool/ ...
- sql时间格式
取值后格式化 {:d}小型:如2005 {:D}大型:如2005年5月6日 {:f}完整型 当前时间获取 DateTime.Now.ToShortDateString 取值中格式化 SQL Serve ...
- 推荐5款超实用的.NET性能分析工具 转
http://www.csdn.net/article/2012-11-23/2812174-5-Good-and-useful-.NET-Profilers
- android ListView下拉刷新 上拉加载更多
背景 最近在公司的项目中要使用到ListView的下拉刷新和上拉加载更多(貌似现在是个项目就有这个功能!哈哈),其实这个东西GitHub上很多,但是我感觉那些框架太大,而且我这个项目只用到了ListV ...
- Part 98 Anonymous methods in c#
What is an anonymous method? Anonymous method is a method without a name. Introduced in C# 2.0,they ...
- shell 脚本执行,出现错误bad interpreter: No such file or directory
出现bad interpreter:No such file or directory的原因是文件格式的问题.这个文件是在Windows下编写的.换行的方式与Unix不一样,但是在VI下面如果不Set ...
- GridView自带分页 1总页数 首页 下一页 上一页 尾页 X 页 go 实现方法 .
在前台GRIDVIEW中添加如下代码 <PagerTemplate> <table> <tr> <td style="text-align: rig ...
- HTML+CSS学习笔记(9)- CSS的继承、层叠和特殊性
标签:HTML+CSS 继承 CSS的某些样式是具有继承性的,那么什么是继承呢?继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代.比如下面代码:如某种颜色应用于p标签,这 ...
- IOS版应用商店应用源码
app商店 swift版 用swift编写的 应用商店 支持iPad iPhone利用了ios8过渡动画 支持横竖屏操作 源码下载: http://code.662p.com/view/11384.h ...