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语言库的创建和调用的更多相关文章

  1. Windows下静态库、动态库的创建和调用过程

    静态库和动态库的使用包括两个方面,1是使用已有的库(调用过程),2是编写一个库供别人使用(创建过程).这里不讲述过多的原理,只说明如何编写,以及不正确编写时会遇见的问题. //注:本文先从简单到复杂, ...

  2. QT共享库的创建与调用(初级)(附:UI界面不能被改变的其中一个原因)

    背景: 最近在做的一个项目其中一部分既是实现PC与下位机的USB通信.windows平台下已经完成,现需移植到linux平台下. 在linux系统中,通过一段时间的工作,设备已被配置成hid类(后续再 ...

  3. Qt 共享库(动态链接库)和静态链接库的创建及调用

    前言: 编译器 Qt Creator, 系统环境 win7 64 位 1.创建共享库: 新建文件或项目->选择 Library 和 c++ 库->选择共享库->下一步(工程名为 sh ...

  4. linux下的C语言开发 进程创建 延伸的几个例子

    在Linux下面,创建进程是一件十分有意思的事情.我们都知道,进程是操作系统下面享有资源的基本单位.那么,在linux下面应该怎么创建进程呢?其实非常简单,一个fork函数就可以搞定了.但是,我们需要 ...

  5. linux静态与动态库创建及使用实例

    一,gcc基础语法: 基本语法结构:(由以下四部分组成) gcc -o 可执行文件名 依赖文件集(*.c/*.o) 依赖库文件及其头文件集(由-I或-L与-l指明) gcc 依赖文件集(*.c/*.o ...

  6. LINUX下C语言编程调用函数、链接头文件以及库文件

    LINUX下C语言编程经常需要链接其他函数,而其他函数一般都放在另外.c文件中,或者打包放在一个库文件里面,我需要在main函数中调用这些函数,主要有如下几种方法: 1.当需要调用函数的个数比较少时, ...

  7. Linux库的创建和使用

    Linux库的概念 库是一种软件组建技术,里面封装了数据和函数,提供给用户程序调用.使用库能够使程序模块化,提高编译速度,实现代码重用,易于升级. Windows系统提供了大量静态链接库(.lib)和 ...

  8. linux下的静态库创建与查看,及如何查看某个可执行依赖于哪些动态库

    linux下的静态库创建与查看,及如何查看某个可执行依赖于哪些动态库   创建静态库:ar -rcs test.a *.o查看静态库:ar -tv test.a解压静态库:ar -x test.a 查 ...

  9. MySQL进阶11--DDL数据库定义语言--库创建/修改/删除--表的创建/修改/删除/复制

    /*进阶 11 DDL 数据库定义语言 库和表的管理 一:库的管理:创建/修改/删除 二:表的管理:创建/修改/删除 创建: CREATE DATABASE [IF NOT EXISTS] 库名; 修 ...

随机推荐

  1. C# Exception的子类Serializable警告

    编译时发出的警告:警告 CA2237[1] 将 [Serializable] 添加到 'HardwareException',原因是此类型实现了 ISerializable. Cause[1] An ...

  2. 十一、Android学习笔记_AsyncQueryHandler的应用

    研究AsyncQueryHandler这个类的时候遇到了几个重要的不清楚的知识点 1. Handler与Thread,Looper的关系 2. HandlerThread是干什么用的 3. Threa ...

  3. Part 16 Important concepts related to functions in sql server

    Important concepts related to functions in sql server

  4. Part 7 Joins in sql server

    Joins in sql server Advanced or intelligent joins in sql server Self join in sql server Different wa ...

  5. 【网络收集】order by 自定义排序

    使用order by排序,有时候不是根据字符或数字顺序,而是根据实际要求排序. 例如有客户A,B,C,我希望排序结果是B,C,A,那么就要通过自定义的规则排序. 第一种方法,可以构造一张映射表,将客户 ...

  6. ORACLE 数据库概述以及Oracel数据库的安装、卸载、使用

    一:Orcale简介 1.发展史 1978年,Orcale诞生 1982年,Orcale3推出了,它是第一个能够运行在大型机和小型机上的关系型数据库 1997年,Orcale公司推出了基于java语言 ...

  7. IE6中position:fixed无效问题解决

    在做页面右下脚对话框时,直接使用position:fixed;大部分浏览器很容易就能做到,但是在IE6中却发现不行,原来是IE6不支持position:fixed;这个属性. 虽然用JS肯定能解决这个 ...

  8. dataAdapter与dataSet和dataTable的填充

    对于dataAdapter与dataSet和dataTable的填充,可以分为1对1,1对n,n对n,3种情况. 以SqlDataAdapter为例. //(1)1对1 SqlDataAdapter ...

  9. HTML+CSS学习笔记 (14) - 单位和值

    标签:HTML+CSS 颜色值 在网页中的颜色设置是非常重要,有字体颜色(color).背景颜色(background-color).边框颜色(border)等,设置颜色的方法也有很多种: 1.英文命 ...

  10. Liskov替换原则(LSP)

    OCP中,继承支持了抽象和多态特性. LSP:子类必须能够替换掉其基类. 反例:使用if/else判断类型,以便选择针对特定类型的正确行为. 有效性并非本质属性 模型的有效性,只能通过它的客户程序来表 ...