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 为了在同一系统中使用不同版本的库,可以在库文件名后加上版本号为后缀,例如: ...
随机推荐
- 在虚拟环境下运行 Confluence 6
这个页面针对运行在虚拟硬件环境下的 Confluence 提供一些信息. 概要 在虚拟环境(virtual machine (VM))下运行 Confluence 需要一些特定的技能来进行设定和进行管 ...
- 机器学习 之梯度提升树GBDT
目录 1.基本知识点简介 2.梯度提升树GBDT算法 2.1 思路和原理 2.2 梯度代替残差建立CART回归树 1.基本知识点简介 在集成学习的Boosting提升算法中,有两大家族:第一是AdaB ...
- 原生sql实现restful接口调用
index.php <?php include './Request.php';include './Response.php';//获取数据$data=Request::getRequest( ...
- node.js面试题大全-侧重后端应用与对Node核心的理解
Node是搞后端的,不应该被被归为前端,更不应该用前端的观点去理解,去面试node开发人员.所以这份面试题大全,更侧重后端应用与对Node核心的理解. github地址: https://github ...
- Vue(五) 购物车案例
这一篇把之前所学的内容做一个总结,实现一个购物车样例,可以增加或者减少购买数量,可移除商品,并且购物车总价随着你的操作实时变化. 实现效果如图: 代码: <!DOCTYPE html> & ...
- 小白的python之路10/22 day1
一.操作系统 操作系统就是一个协调.管理和控制计算机硬件资源和软件资源的控制程序.操作系统所处的位置如下图
- request.getRealPath的替代方法
在写上传小练习的时候,发现获得路径的request.getRealPath("")已经被画上线了,也就是不再建议使用. package controller; import jav ...
- Windows查看Java内存使用情况
Windows查看Java程序运行时内存使用情况 1.在cmd命令窗口输入 jconsole ,弹出Java监视和管理控制台窗口 2.连接本地进程,首先需要知道想查看的进程ID ( pid ) 在c ...
- php解析Excel表格并且导入MySQL数据库
最近根据客户需求,需要增加一个导入Excel表格的功能,Excel中存放的是知识库中医知识的分类体系目录.是在thinkphp框架下编写的代码,用的是phpexcel第三方包.测试环境用的是xampp ...
- kafka分区及副本在broker的分配
kafka分区及副本在broker的分配 部分内容參考自:http://blog.csdn.net/lizhitao/article/details/41778193 以下以一个Kafka集群中4个B ...