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)" (?f@@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 f@@YAXXZ(void);(f后面的取决于编译器),而C眼中,f就是f。一般需要使用extern "C"来使用,或者直接把hello.c改后缀为hello.cpp。
linux上静态库和动态库的编译和使用(附外部符号错误浅谈)的更多相关文章
- Linux上静态库和动态库的编译和使用
linux上静态库和动态库的编译和使用(附外部符号错误浅谈) 这就是静态库和动态库的显著区别,静态库是编译期间由链接器通过include目录找到并链接到到可执行文件中,而动态库则是运行期间动态调用,只 ...
- C语言静态库与动态库(Windows下测试)
转载于:https://zhidao.baidu.com/question/1946953913764139388.html,原文为Linux上测试,本文为在Windows上编译测试 我们通常把一些公 ...
- 【C/C++开发】C++静态库与动态库以及在Linux和Windows上的创建使用
原文出处: 吴秦的博客 这次分享的宗旨是--让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择.这里不深入介绍静态库.动态库的底层格式,内存布局等,有兴趣的同学 ...
- Linux下Gcc生成和使用静态库和动态库详解(转)
一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同( ...
- 在Linux下如何使用GCC编译程序、简单生成 静态库及动态库
最近在编写的一个Apache kafka 的C/C++客户端,,在看他写的 example中,他的编译是用librdkafka++.a和librdkafka.a 静态库编译的,,,而我们这 ...
- Linux下Gcc生成和使用静态库和动态库详解
参考文章:http://blog.chinaunix.net/uid-23592843-id-223539.html 一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库 ...
- [转]Linux下用gcc/g++生成静态库和动态库(Z)
Linux下用gcc/g++生成静态库和动态库(Z) 2012-07-24 16:45:10| 分类: linux | 标签:链接库 linux g++ gcc |举报|字号 订阅 ...
- 在Linux中创建静态库和动态库
我们通常把一些公用函数制作成函数库,供其它程序使用. 函数库分为静态库和动态库两种. 静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库. 动态库在程序编译时并不会被连接到目标代码中 ...
- 详细讲解 关于Linux静态库和动态库的分析
基本概念 库有动态与静态两种,动态通常用.so为后缀,静态用.a为后缀. 例如:libhello.so libhello.a 为了在同一系统中使用不同版本的库,可以在库文件名后加上版本号为后缀,例如: ...
随机推荐
- BFS 路径记录
有一迷宫 N*M,要求输出可通行的最短路径. 可以先倒着 BFS 一遍迷宫,这样 dis[] 数组储存的就是各点到迷宫终点的最短距离. 然后再从起点开始 BFS 一遍 dis[] ,只要满足 dis[ ...
- Grafana + Prometheus 监控PostgreSQL
效果图 部署环境 服务器名称 IP地址 部署业务 备注 部署agent sht-sgmhadoopcm-01 172.16.101.54 PostgreSQL 监控服务器.被监控服务器 node_ex ...
- linux 使用split分割大文件
1.分割 -- split命令 可以指定按行数分割和按字节大小分割两种模式. (1) 按行数分割 $ split -l 300 large_file.txt new_file_prefix 加上-d, ...
- python --常用内置模块01
1.简单了解模块 模块就是我们把装有特定功能的代码进行归类的解构,从代码编写的单位来看我们的程序 从小到大的顺序:一条代码< 语句块<代码块(函数,类) < 模块 我 ...
- python安装requests
下面是requests的安装步骤: 1.如果系统已经装了Python,把D:\python3.6.5\Scripts添加到系统的环境变量PATH后面 2.cmd下cd到这个目录下D:\Python3. ...
- Saiku关于MDX过滤的使用(九)
Saiku查询设定:Saiku查询数据时,每次都是全量查询的,我们现在需要默认展示近一周的数据. 通过编写使用MDX表达式进行过滤 通过编写MDX表达式,添加新的指标信息对一周以内的数据进行标识 (其 ...
- ECharts访问后台,JSON格式返回数据实例
完成图 一.页面代码 <%@ page language="java" contentType="text/html; charset=UTF-8" pa ...
- L2-014. 列车调度(set)*
L2-014. 列车调度 参考博客 #include <iostream> #include <cstdio> #include <set> #include &l ...
- C#中字符串大小比较函数--CompareTo与Compare方法(需要完善补充)
字符串比较的原理是什么? 原理: 从两个字符串的第一个字符开始逐个进行比较(按字符的ASCII值进行大小比较),直到出现不同的字符或遇到‘\0’为止. 如果全部字符都相同,就认为两字符串相等,返回0: ...
- VS2008 快捷键大全
转载自 https://www.cnblogs.com/likebeta/archive/2013/02/20/2919224.html Ctrl+E,D ---- 格式化全部 ...