linux上静态库和动态库的编译和使用(附外部符号错误浅谈)

主要参考博客gcc创建和使用静态库和动态库

对于熟悉windows的同学,linux上的静态库.a相当于win的.lib,动态库.so相当于win的.dll.

首先简要地解释下这两种函数库的区别,参考《Linux程序设计》

1. 静态库也被称为归档文件(archive,因此创建命令是ar),编译器和链接器负责将程序代码和静态库结合在一起组成单独的可执行文件;

但是缺点是许多应用程序同时运行并使用来自同一个静态库的函数时,内存中就会有一个函数的多份副本,而且程序文件自身也有多份同样的副本,这将消耗大量的内存和磁盘空间。

2. 动态库,也称共享库(因此创建命令包含share)。可执行文件不会包含动态库的函数代码,而是引用运行时可访问的共享代码,函数引用被解析并产生对动态库的调用时,动态库才会被加载到内存中。因此系统可以只保留动态库的一份副本,并且当函数功能需要改变时,只需要重新编译生成动态库,而不用重新编译整个源程序。

现在直接进入重点,贴代码。首先我创建了三个文件hello.h hello.c main.c,其中前两个是函数hello()的头文件和源文件,main.c则是调用hello()函数。代码如下

/*************************************************************************
    > File Name: hello.h
 ************************************************************************/
#ifndef _HELLO_H_
#define _HELLO_H_

void hello();

#endif
/*************************************************************************
    > File Name: hello.c
 ************************************************************************/

#include <stdio.h>
#include "hello.h"

void hello()
{
	printf("hello world!\n");
}
/*************************************************************************
    > File Name: main.c
 ************************************************************************/
#include "hello.h"

int main(int argc, char** argv)
{
    hello();
    return 0;
}

目录组织如下(这里使用了tree,我是Ubuntu系统,命令apt-get install tree即可安装)

两个sh文件分别是使用动态库和静态库的shell文件,把命令行整合到一起,代码如下

#########################################################################
# File Name: static-compile.sh
#########################################################################
#!/bin/bash
cd ./lib
gcc -c -I../include hello.c  # 生成hello.o
ar rc libhello.a hello.o  # 生成libhello.a
cd ../src
gcc main.c -I../include -L../lib -lhello -o main  # 生成main
cd ../  # 回到根目录

首先进入lib目录把自定义函数库的源文件hello.c进行编译生成目标文件hello.o。然后用ar rc(ar是归档,rc分别代表replace和create,即若已存在则替换、创建新文件)生成静态库libhello.a。

然后进入src目录,-I后面紧接着(没有空格)头文件目录路径(I代表include),-L后面紧接着(没有空格)库文件目录路径(L代表lib)

#########################################################################
# File Name: shared-compile.sh
#########################################################################
#!/bin/bash
cd ./lib
# 生成动态库libhello.so
gcc hello.c -I../include -fpic -shared -o libhello.so
# 生成可执行文件
cd ../src
mv ../lib/libhello.so ./
cp ./libhello.so /lib  # 将动态库复制到/lib文件夹
gcc main.c -I../include -L../lib -lhello -o main cd ../  # 回到根目录

对于动态库来说,生成命令多了-fpic和-shared。PIC代表Position-Independent Code,与位置无关,也就是使用的都是相对路径和绝对路径。shared代表共享。

而在使用动态库之前,需要把.so复制到/lib文件夹(或者设置环境变量,见我文章开头引用的博客)。

使用动态库的命令除了-I和-L外,还有个-l(小写的L),后面接着的是hello。

——这是因为我的动态库命名为libhello.so,使用-l的话会忽略前面的lib和后面的后缀名。

分别在linux下运行static-compile.sh和shared-compile.sh,效果如下

由于动态库没有把函数库的代码加入到可执行文件中,所以可以看出使用动态库链接出的程序大小偏小(8592<8664)

当然,生成程序之后,.a、.so(非/lib目录下的)文件都可以删掉,程序一样能运行。

最后谈下这两者的实际应用,就以Visual C++编程来谈吧。

很多时候会出现unsolved external symbol(未解决的外部符号)错误,如果是用其他人的库,很有可能就是忘记在菜单设置-链接器->输入->附加依赖项中加入需要的.lib文件(也就是静态库)。(比如使用winsock2.h的一些库函数时,没有#pragma comment(lib, "Ws2_32.lib"))把Ws2_32.lib静态库加载进去的话,函数就只有头文件中的声明,而缺少了库文件中的定义。

而如果是忘记把.dll路径(往往是bin文件夹)添加到环境变量中(PS:我的dynamic-compile.sh对应windows相当于是把dll放到了system32目录下),在编译的时候不会出错,而是Debug运行的时候会报错。

这就是静态库和动态库的显著区别,静态库是编译期间由链接器通过include目录找到并链接到到可执行文件中,而动态库则是运行期间动态调用,只有运行时找不到对应动态库才会报错。

说回外部符号错误,如果是自己写的,很有可能就是函数只编译了,没有定义。比如f.h中写的是void f(); 结果对应的f.cpp写的是void f2() {}看起来不可能出现这种错误,实际上由于C++重载某种意义上是改了函数名(而且还改得很长),这样的错误对新手来说很常见。

还有个典型的例子,就是C++调用C函数,报错如下

main.obj : error LNK2019: unresolved external symbol "void __cdecl f(void)" ([email protected]@YAXXZ) referenced in function _main
// hello.h
#pragma once

void f();
// hello.c
#include "hello.h"

void f() { }
// main.cpp
#include "hello.h"

int main()
{
	f();
	return 0;
}

因为C++眼中,void f(void);其实是void [email protected]@YAXXZ(void);(f后面的取决于编译器),而C眼中,f就是f。一般需要使用extern "C"来使用,或者直接把hello.c改后缀为hello.cpp。

时间: 2024-10-14 11:05:30

linux上静态库和动态库的编译和使用(附外部符号错误浅谈)的相关文章

Linux上静态库和动态库的编译和使用

linux上静态库和动态库的编译和使用(附外部符号错误浅谈) 这就是静态库和动态库的显著区别,静态库是编译期间由链接器通过include目录找到并链接到到可执行文件中,而动态库则是运行期间动态调用,只有运行时找不到对应动态库才会报错 gcc创建和使用静态库.动态库 gcc动态链接库*.so文件的生成与使用方法 原文地址:https://www.cnblogs.com/gdut-gordon/p/10390532.html

Linux使用静态库和动态库

Linux使用静态库和动态库 (一)库的概念 库是可以复用的代码,在一些大的项目中常常会用到库. 本质上说:库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 一般说库是说两种: 静态库:linux下.a文件.windows下.lib文件 动态库:linux下.so文件.windows下.dll文件 最近花了一些时间把linux下编译.链接等研究了一下,作为一个菜鸟记录并分享一蛤. (二)静态库与动态库 程序的编译运行要经过以下步骤: 1.源文件(.h .cpp等) 2.预编译 3.编

Linux下Gcc生成和使用静态库和动态库详解

参考文章:http://blog.chinaunix.net/uid-23592843-id-223539.html 一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同(主要是编译器.汇编器和连接器的不同),因此二者库的二进制是不兼容的. 本文仅限于介绍linux下的库. 1.2库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同

【转】Linux下gcc生成和使用静态库和动态库详解

一.基本概念 1.1 什么是库 在Windows平台和Linux平台下都大量存在着库. 本质上来说,库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同(主要是编译器.汇编器和连接器的不同),因此二者的库的二进制是不兼容的. 本文仅限于介绍linux下的库. 1.2 库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同点在于代码被载入的时刻不同. 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大. 共享库的代码是在可

linux成长之路(gcc编译器、静态库、动态库)

Jeremy Lin GCC简介 GCC(GNU Complier Collection)是GNU推出的功能强大.性能优越的多平台编译器套件,它包括了C.C++.Objective-C.Fortran.Java.Ada和Go语言的前端,也包括了这些语言的库,当前最新的版本是GCC 5.1.GCC可以在多种硬件平台上编译出可执行程序,其执行效率与一般的编译器相比平均效率要高20%-30%.GCC编译器能将C.C++语言源程序.汇程式程序和目标程序编译.连接成可执行文件,如果没有给出可执行文件的名字

[转]Linux下用gcc/g++生成静态库和动态库(Z)

Linux下用gcc/g++生成静态库和动态库(Z) 2012-07-24 16:45:10|  分类: linux |  标签:链接库  linux  g++  gcc  |举报|字号 订阅 在 linux 下,库文件一般放在 /usr/lib 和 /lib 下, 静态库的名字一般为 libxxxx.a ,其中 xxxx 是该 lib 的名称 动态库的名字一般为 libxxxx.so.major.minor , xxxx 是该 lib 的名称, major 是主版本号, minor 是副版本号

Linux下静态库与动态库

一.基本概念 1.1.什么是库        在 windows 平台和 linux 平台下都大量存在着库. 本质上来说库是一种可执行的二进制代码(但不可以独立执行),可以被操作系统载入内存执行. 由于 windows 和 linux 的平台不同(主要是编译器.汇编器和连接器 的不同),因此二者库的二进制是不兼容的. 本文仅限于介绍 linux 下的库. 1.2. 库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同点在于代码被载入的时刻不同: 静态库的代码在编译过程中已经被

关于Linux静态库和动态库的分析

关于Linux静态库和动态库的分析 关于Linux静态库和动态库的分析 1.什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的本质不同,因此二者库的二进制是不兼容的. 本文仅限于介绍linux下的库. 2.库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同点在于代码被载入的时刻不同. 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大. 共享库的代

linux下静态库和动态库一些东西

http://www.cnblogs.com/changefuture/archive/2011/12/22/2297460.html Linux  动态链接库和静态库示例 文件预览 文件目录树如下,如你所见,非常简单. libtest/ |-- lt.c |-- lt.h `-- test.c 代码 #lt.c /* lt.c * */ #include <stdio.h> void myprint(void) { printf("Linux library test!\n&quo