02_vim_gcc_staticLib_dynamicLib

代码: https://github.com/WHaoL/study/tree/master/00_06_Linux_SystemCode_and_SocketCode

代码: https://gitee.com/liangwenhao/study/tree/master/00_06_Linux_SystemCode_and_SocketCode

1. vim

安装vim

sudo apt install vim

sudo yum install vim

自带帮助文档:

  • 在终端上执行命令: vimtutor

1.1 vim的模式

使用vim将文件打开, 默认是命令模式

在这里插入图片描述

  • 命令模式

    • 通过命令对文件内容进行修改
      • 复制 yy nyy
      • 粘贴 p
      • 删除行 dd
      • 保存,并退出 ZZ (ctrl+z ctrl+z)
      • 代码格式化 gg=G
      • 移动到行首 0
      • 移动到行尾 shift+4
      • 移动到文件头 gg
      • 移动到文件尾 G
      • 行跳转 行号+G
      • 从当前行向下跳转对应的行数 数字(行数)+回车
      • h: 光标左移
        j: 光标下移
        k: 光标上移
        l: 光标右移
      • 撤销 u
      • 反撤销 ctrl+r
      • 查找 / 待搜索的字符串 n 正序 N 逆序
  • 文件编辑模式

    • 在文件中进行写操作

      • 命令模式 切换到 文本编辑模式 i
      • 文本编辑模式 切换到 命令模式 Esc
  • 末行模式

    • 在终端的最下边一行中输入一些命令对文件进行操作

      • 命令模式 切换到 末行模式 :
      • 末行模式 切换到 命令模式 连续两次Esc
      • 保存, 并退出 :wq
      • 替换字符串 :%s/待替换的字符串/替换成什么字符串/g (替换所有匹配的)
      • 行跳转 :行号
      • 执行shell命令 :!shell命令
      • 分屏
      • 数据查找
      • 内容的替换

1.2 命令模式

  • 保存退出

    # 两个大写的Z, 在键盘上输入
    ZZ
    Ctrl+z Ctrl+z
    
  • 代码格式化

    # 在键盘上输入:
    gg=G
    
  • 光标移动

    # 1. 上下左右移动
    h: 光标左移
    j: 光标下移
    k: 光标上移
    l: 光标右移
    # 2. 移动到行首
    行首: 0
    # 3. 移动到行尾
    行尾: shift+4
    # 4. 移动到文件头 -> 第一行的行首
    gg
    # 5. 移动到文件尾 -> 最后一行行首
    G
    # 6. 行跳转
    行号G
    # 从当前行向下跳转对应的行数
    数字(行数)+回车
    
  • 删除命令

    # 1. 字符删除
    	- 删除光标盖住的字符(当前字符): x (小写)
    	- 删除光标盖住的字符前边的字符: X	(大写)
    # 2. 删除单词
    	- dw: 将光标放到要删除的单词的最前边, 用光标盖住单词的第一个字符, 否则只能删除一部分
    # 3. 删除光标前的字符串
    	- d0
    # 4. 删除光标后的字符串 (两种方式)
    	- d$
    	- D
    # 5. 删除行
    	- dd
    # 6. 删除多行
    	- 行数dd (ndd)
    		- 10dd # 一次删除10行, 从光标所在的行向下
    
  • 撤销和反撤销

    # 1. 撤销
    u
    # 2. 反撤销
    ctrl+r
    
  • 复制和粘贴

    # 复制 -> 复制光标所在的当前行
    yy
    # 复制N行
    nyy (n是要复制的行数, 从当前行向下复制)
    
    # 粘贴 -> 粘贴到光标所在行的下一行
    p
    # 粘贴 -> 粘贴到光标所在行的上一行
    P
    
  • 可视模式

    # 在可以看到的情况下, 进行行列的选择, 进行复制, 删除
    # 进入到可视模式: 
    v
    # 如何选择:
    h: 光标左移
    j: 光标下移
    k: 光标上移
    l: 光标右移
    # 删除
    d
    # 复制
    y
    # 将可视模式中复制的内容进行粘贴
    p: 粘贴到当前光标的后边
    P: 粘贴到当前光标的前边
    
  • 替换操作

    # 替换某一个字符, 光标在哪个字符上, 就替换哪个字符, 其余的字符无法操作
    r
    # 从光标所在的当前字符开始覆盖原字符, 输入多少新字符, 覆盖多少旧的字符
    R
    
  • 查找命令

    # 搜索
    # 1. 从上往下搜索
    先按键盘上的 / 再输入要搜索的字符串
    # 搜索到的字符会加底色显示, 我需要遍历查看
    n: 从上到下 -> 正序
    N: 从下到上 -> 逆序
    
    # 2. 从文件尾部向上搜索
    先按键盘上的 ? 再输入要搜索的字符串
    
    # 3. 找到要搜索的关键字, 将光标放到这个关键字上, 在键盘上输入#
    使用N/n进行遍历
    
  • 查看man文档

    # man首页
    man man# 在vim中直接跳转到man文档
    # 将光标放到某一个函数上边, 输入章节号 + shift+k
    

1.3 文本编辑模式

命令模式切换到文本编辑模式

a -> 从当前光标的后边开始插入数据
A -> 从当前行的后边开始插入数据

i -> 从当前光标的前边开始插入数据 #################
I -> 从当前行行首开始插入数据

o -> 在当前行下边创建新的行进行数据插入
O -> 在当前行上边创建新的行进行数据插入

s -> 删除光标盖住的字符, 从删除的字符的位置开始插入数据
S -> 删除当前行, 从该行进行数据插入

# 从文本编辑模式 -> 命令模式
esc

1.4 末行模式

  • 命令模式切换到末行模式

    # 按键盘上的符号 
    # 命令模式 -> 末行模式 
    :
    # 末行模式 -> 命令模式
    	- 连续按两次esc
    	- 在末行模式下执行一个指令
    
  • 保存退出

    # 保存, 不退出
    :w
    
    # 保存, 并退出
    :wq == x
    
    # 退出不保存
    # 如果我们修改了文件, 使用q退出, 会提示我们保存
    :q
    
    # 强制退出不保存
    :q!
    
  • 替换

    # 对文件中的指定字符串进行替换
    ########################################
    # 替换光标所在行中的第一个符合条件的字符串
    :s/待替换的字符串/替换成什么字符串
    # 替换光标所在行中的所有字符串
    :s/待替换的字符串/替换成什么字符串/g
    
    # 替换指定行中的字符串(需要指定一个范围)
    :2,10s/待替换的字符串/替换成什么字符串		# 这些行的第一个符合条件的字符串
    :2,10s/待替换的字符串/替换成什么字符串/g	# 替换所有
    
    # 整个文件范围内都替换
    :%s/待替换的字符串/替换成什么字符串	# 所有行中的第一个符合条件的字符串
    
    
    ########################################
    # 替换文本中,所有复合条件的字符串
    :%s/待替换的字符串/替换成什么字符串/g	# 替换所有!!!!!
    
  • 分屏

    # 将文件分成多个屏幕显示
    # 水平分屏
    :sp
    # 垂直分屏
    :vsp
    
    # 拆分的屏幕之间的切换
    ctrl+w+w
    
    # 同时关闭多个屏幕
    :qall
    # 保存并退出多个屏幕
    :wqall
    # 保存多个屏幕中的文件数据
    :wall
    
    # 分屏的时候在不同的屏幕中打开不同的文件
    :sp 文件名
    :vsp 文件名
    
    # 使用vim打开文件的时候指定多个文件分屏显示
    # 参数o, 水平分屏
    vim -o file1 file2 file3 ...	
    # 参数O, 垂直分屏
    vim -O file1 file2 file3 ...	
    
  • 行跳转

    :行号
    
  • 执行shell命令

    :!shell命令
    # 举例
    :!ls
    

1.5 vim配置文件

# 用户级别 -> 对当前用户生效
~/.vimrc
# 系统级别 -> 对所有用户生效
/etc/vim/vimrc

# 都配了, 用户级别生效

2.gcc

在这里插入图片描述

GNU编译器套件(GNU Compiler Collection)原名為GNU C語言编译器GNU C Compiler),因為它原本只能处理C語言。GCC在发布后很快地得到扩展,包括C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)。GCC的初衷是为GNU操作系统专门编写的一款编译器。GNU系统是彻底的自由软件。此处,“自由”的含义是它尊重用户的自由。

GUN 编译器套件包含多种前端处理器,以翻译各种不同语言。当然,我们重点讨论的是基于C语言的前端处理器 GCC。

GCC 也是一种多目标(multitarget)编译器;换句话说,它通过使用可互换的后端处理器,为多种不同的计算机架构生成相应的可执行程序。

GCC 不仅支持C的许多“方言”,也可以区别不同的C语言标准;也就是说,可以使用命令行选项来控制编译器在翻译源代码时应该遵循哪个C标准。例如,当使用命令行参数-std=c99启动 GCC 时,编译器支持 C99 标准。

GUN99 c99

在linux下使用 gnu打头的标准: gun99 gun11

1.1 安装

  • 安装命令

    # gcc用于编译c程序, g++编译c++程序
    sudo apt install gcc g++
    sudo yum install gcc g++
    
    # 在Linux-CentOS7-1804中写程序: gcc版本 > 4.8.5
    
  • 查看版本

    gcc -v
    gcc --version
    g++ -v
    g++ --version
    
    # 如果写c++程序, 并且使用c++11中的语法, 编译器版本>4.8.5
    
  • gcc与g++有什么区别?

    gcc和g++都是GNU(组织)的一个编译器。

    • 误区一:gcc只能编译c代码,g++只能编译c++代码。两者都可以,但是请注意:

      • 后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;
      • 后缀为.cpp的,两者都会认为是c++程序,C++的语法规则更加严谨一些。
      • 编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的。
    • 误区二:gcc不会定义__cplusplus宏,而g++会

      • 实际上,这个宏只是标志着编译器将会把代码按C还是C++语法来解释
      • 如上所述,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义。
    • 误区三:编译只能用gcc,链接只能用g++

      • 严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用g++或者gcc -lstdc++
      • gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。但在编译阶段,g++会自动调用gcc,二者等价

1.2 gcc 工作流程

在这里插入图片描述

// 1. 预处理
/*
	- 头文件展开
	- 宏替换
	- 注释删除
*/
// test.c -> test.i
gcc -E test.c -o test.i

// 2. 编译 -> 得到汇编文件
gcc -S test.i -o test.s

// 3. 汇编 -> 二进制文件
gcc -c test.s -o test.o

// 4. 链接 -> 二进制文件, 可执行
// 如果不指定生成的文件名, 默认a.out
gcc test.o

1.3 gcc常用参数选项

gcc编译选项 选项的意义
-E 预处理指定的源文件,不进行编译
-S 编译指定的源文件,但是不进行汇编
-c 编译、汇编指定的源文件,但是不进行链接
-o [file2] [file1] / [file1] -o [file2] 将文件 file1 编译成可执行文件 file2
-I directory 指定 include 包含文件的搜索目录
-g 在编译的时候,生成调试信息,该程序可以被调试器调试
-D 在gcc/g++程序编译的时候,指定一个宏
-w 不生成任何警告信息
-Wall 生成所有警告信息
-On n的取值范围:0~3。编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
-l 在程序编译的时候,指定使用的库
-L 指定编译的时候,搜索的库的路径。
-fPIC/fpic 生成与位置无关的代码
-shared 生成共享目标文件。通常用在建立共享库时
-std 指定C方言,如:-std=c99,gcc默认的方言是GNU C
#############gcc常用参数
 -g         # 生成调试信息
 -w         # 不成成警告信息
 -I         # 指定头文件的搜索路径
 -l         # 指定库名
 -L         # 指定库的搜索路径
 -fPIC      # 生成与位置无关的代码
 -shared    # 生成动态库目标文件
 -std=c11   # 指定GNU c的版本
 -std=c99   # 指定GNU c的版本
  -D        # 调试时,指定宏

CFLAGS      # 执行“ CC”编译器的命令行参数(编译.c源文件的选项)
CXXFLAGS    # 执行“ g++”编译器的命令行参数(编译.cc源文件的选项)
LDFLAGS     # 链接器(如:“ ld”)参数
#include <stdio.h>
int main()
{
    
    
    int a = 10;
#ifdef DEBUG
    printf("我是一个程序猿, 我不会爬树...\n");
#endif
    for(int i=0; i<3; ++i)
    {
    
    
        printf("hello, GCC!!!\n");
    }
    return 0;
}
// 参数 -D 使用场景: 程序调试的时候使用, 通过-D指定的宏打开log输出
// 默认编译第12行代码不输出, 如果想要输出这句话, 可以使用-D
gcc test_-DDEBUG.c -D DEBUG
gcc test_-DDEBUG.c -o test_-DDEBUG  -std=c11 -DDEBUG

    
// 参数 -On 优化程序, 有4个级别
// n 取值范围: 0-3
// O0 -> 不优化, O1 -> 默认的级别, O3 -> 最高级别
// 最简单的例子:
int a, b, c, d;
a = 10;
b = a;
c = b;
d = c;

// 优化:
b = 10;
c = 10;
d = 10;



// 参数 -std , 指定编译器规范
// c最早的标准:c89
// c99开始支持现在的书写习惯

int a = 10;
func();
int b  =5;
for(int i=0; i<5; ++i)

gcc -std=c99/gnu99 test_-DDEBUG.c -o test_-DDEBUG
-DDEBUG 参数和值之间的空格可有可无
gcc test_-DDEBUG.c -o test_-DDEBUG -DDEBUG   # define DEBUG

3 静态库和动态库

2.1 扫盲

  • 库是什么?
    • 是一个文件
    • 二进制的
    • 二进制数据 -> 函数的实现(函数定义)
  • 为什么要使用库?
    • 保密的需要
    • 方便分发和部署
  • 库有了如何使用?
    • 库中定义的函数对应头文件
    • 生成的库文件

2.2 静态库

  • 命名规则

    • Linux
      • libxxx.a
        • lib -> 前缀, 固定的
        • xxx -> 库的名字, 随意指定
        • .a -> 后缀, 固定的
    • windows
      • libxxx.lib
  • 静态库制作

在这里插入图片描述

# 将源文件 -> 静态库
# 原材料
.
├── add.c
├── div.c
├── include
│   └── head.h
├── main.c
├── mult.c
└── sub.c

# 1. 将.c -> .o
$ gcc -c add.c div.c mult.c sub.c -I ./include/
$ ls
add.c  add.o  div.c  div.o  include  main.c  mult.c  mult.o  sub.c  sub.o

# 2. 将生成的.o 打包成静态库
# ar rcs 生成的库的名字 原材料(*.o)
# 	r: 替换
#	c: 创建
# 	s: 索引
$ ar rcs libcalc.a *.o
robin@OS:~/Linux/3Day/calc$ ls
add.c  add.o  div.c  div.o  include  `libcalc.a`  main.c  mult.c  mult.o  sub.c  sub.o

# 发布
将头文件`head.h`和静态库`libcalc.a`

###########################################
#步骤如下:
#1. 将.c -> .o
gcc -c add.c div.c mult.c sub.c -I ./include/
#2. 将生成的.o 打包成静态库
ar rcs libcalc.a *.o
#3. 发布
head.h  libcalc.a
###########################################

1. 生成libXXX.a的Makefile

# 根据 *.c 和 .h
# 生成静态库 libcalc.a

#---------------------------------------------------------
# 工程名 / 库名 # 生成静态库 libcalc.a  计算简单的加减乘除
PROJECT     = libcalc
OUTNAME     = $(PROJECT).a
#---------------------------------------------------------
# 头文件目录
INCLUDEPATH = -I./include/
#---------------------------------------------------------
# .o文件
SrcSuf      = c
ObjSuf      = o
LibSuf      = a

OBJFILES    = ./add.$(ObjSuf)\
              ./div.$(ObjSuf)\
              ./mult.$(ObjSuf)\
              ./sub.$(ObjSuf)\
#---------------------------------------------------------

CC          = gcc
LD          = ar rcs

# 编译选项
CFlag       = -w -g -ggdb -fshort-wchar -std=c11 $(INCLUDEPATH)
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf)

all:  $(PROJECT) clean

$(PROJECT):$(OBJFILES)
	@echo "Linking $(OUTNAME) start..."
	$(LD) $(OUTNAME) $(OBJFILES)
	@echo "Linking $(OUTNAME) end"

clean:
	@echo "Cleaning $(PROJECT) project files"
	@rm -f $(OBJFILES) core

.$(SrcSuf).$(ObjSuf):
	@echo "Compiling $(PROJECT) $<"
	$(CC) $(CFlag) -c $< -o $@ 
  • 静态库使用
# 测试静态库函数是不是能支持工作
# 根据头文件中的函数声明, 对这些函数进行调用, 测试得到的结果
# 测试场景
.
├── head.h		-> 发布的头文件
├── libcalc.a	-> 发布的静态库
└── main.c		-> 编写的测试程序, 测试静态库中的函数

# 编译测试程序
$ gcc main.c -o app
/tmp/cchYTdck.o: In function `main':
main.c:(.text+0x38): undefined reference to `add'
main.c:(.text+0x58): undefined reference to `subtract'
main.c:(.text+0x78): undefined reference to `multiply'
main.c:(.text+0x98): undefined reference to `divide'
collect2: error: ld returned 1 exit status
# 上述错误原因: 只能找到函数声明, 找不到定义, 函数定义在静态库中
# 在编译的时候需要指定静态库
# -L: 静态库存储的路径(相对/绝对)
# -l: 指定库的名字, 掐头(lib), 去尾(.a)
# gcc main.c -o app -L 库的路径 -l库的名字
gcc main.c -o app -L ./ -l calc

2. 使用libXXX.a的Makefile

# 根据main.c libcalc.a head.h 
# 生成可执行文件 execute

#---------------------------------------------------------

# 工程名  计算简单的加减乘除
PROJECT     = execute
#---------------------------------------------------------

# .o文件
SrcSuf      = c
ObjSuf      = o
LibSuf      = a

OBJFILES    = ./main.$(ObjSuf)\
#---------------------------------------------------------

# 头文件目录
INCLUDEPATH = -I../include/
#---------------------------------------------------------

#依赖库所在的目录
LIBPATH= -L../
#---------------------------------------------------------

#依赖的静态库
LIBS=../libcalc.a
#---------------------------------------------------------

CC          = gcc
CFlag       = -w -g -ggdb -fshort-wchar -std=c11 $(INCLUDEPATH)
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf)

all:  $(PROJECT) clean

$(PROJECT):$(OBJFILES)
	@echo "creating $(PROJECT) start..."
	$(CC) $(OBJFILES) -o $(PROJECT)  $(INCLUDEPATH) $(LIBPATH) $(LIBS) 
	@echo "creating $(PROJECT) end"

#%.o:%.c
#	$(CC) -MM $^
#	$(CC) $(CXXFLAG) -c $^ $(INCLUDEPATH)  -o $@ 

clean:
	@echo "Cleaning $(PROJECT) project files"
	@rm -f $(OBJFILES) core

.$(SrcSuf).$(ObjSuf):
	@echo "Compiling $(PROJECT) $<"
	$(CC) $(CFlag) -c $< -o $@ 

2.3 动态库/共享库

共享 -> 共享内存中的库

  • 命名规则

    • linux:
      • libxxx.so
        • lib -> 前缀
        • .so -> 后缀
        • xxx -> 库的名字, 随意
        • 在linux中是一个可执行文件
    • windows:
      • 由vs制作
        • libxxx.dll
        • libxxx.lib
      • 非vs
        • libxxx.dll
  • 动态库制作

在这里插入图片描述

# 源文件 -> 动态库的过程
.
├── add.c
├── div.c
├── include
│   └── head.h
├── main.c
├── mult.c
└── sub.c
# .c -> .o
# -fpic 参数必须要加 == -fPIC
$ gcc -c add.c div.c mult.c sub.c -I ./include/ -fPIC
# .o 打包 -> .so
# 使用了参数: shared
$ gcc -shared -o libcalc.so *.o
robin@OS:~/Linux/3Day/calc$ ls
add.c  add.o  div.c  div.o  include  `libcalc.so`  main.c  mult.c  mult.o  sub.c  sub.o
  • 动态库使用

    # 测试动态库函数是不是能正常工作
    # 根据头文件中的函数声明, 对这些函数进行调用, 测试得到的结果
    # 测试场景
    .
    ├── head.h		-> 发布的头文件
    ├── libcalc.so	-> 发布动态库
    └── main.c		-> 测试程序
    
    # 编译测试程序
    # -L: 动态库的路径
    # -l: 指定动态库的名字, 掐头(lib)去尾(.so)
    $ gcc main.c -L ./ -l calc -o app
    

    查看动态库的依赖

    #1. 查看依赖的库:
    objdump -x libName.so | grep NEEDED
    
    #2.查看动态库的依赖是否完备
    $ ldd libName.so  
    #如果某个依赖的库不存在,会输出类似 OOXX.so not found 字样。
    
    
    
    # 举例如下
    [root@lwh test]# locate libmysqlclient.so
    /usr/lib64/mysql/libmysqlclient.so
    
    [root@lwh test]# ldd /usr/lib64/mysql/libmysqlclient.so
            linux-vdso.so.1 =>  (0x00007ffc731d6000)
            libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f842cf82000)
            libdl.so.2 => /lib64/libdl.so.2 (0x00007f842cd7e000)
            libssl.so.10 => /lib64/libssl.so.10 (0x00007f842cb0c000)
            libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007f842c6a9000)
            librt.so.1 => /lib64/librt.so.1 (0x00007f842c4a1000)
            libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f842c19a000)
            libm.so.6 => /lib64/libm.so.6 (0x00007f842be98000)
            libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f842bc82000)
            libc.so.6 => /lib64/libc.so.6 (0x00007f842b8b4000)
            /lib64/ld-linux-x86-64.so.2 (0x00007f842da9e000)
            libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007f842b667000)
            libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007f842b37e000)
            libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007f842b17a000)
            libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007f842af47000)
            libz.so.1 => /lib64/libz.so.1 (0x00007f842ad31000)
            libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007f842ab21000)
            libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007f842a91d000)
            libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f842a704000)
            libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f842a4dd000)
            libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f842a27b000)
            
    [root@lwh test]# objdump -x  /usr/lib64/mysql/libmysqlclient.so | grep NEEDED
      NEEDED               libpthread.so.0
      NEEDED               libdl.so.2
      NEEDED               libssl.so.10
      NEEDED               libcrypto.so.10
      NEEDED               librt.so.1
      NEEDED               libstdc++.so.6
      NEEDED               libm.so.6
      NEEDED               libgcc_s.so.1
      NEEDED               libc.so.6
      NEEDED               ld-linux-x86-64.so.2
    [root@lwh test]# 
    
  • 运行可执行程序提示找不到动态库

    $ ./app 
    ./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory
    
  • 动态库和静态库是如何工作的

    /*
    	静态库: libcalc.a
    	 - 链接命令: gcc main.c -o app -L ./ -l calc
    	 - 静态库通过上边的命令, 被打包了可执行程序app中
    	 - 启动可执行程序app的时候, 静态库被加载到内存中(代码区), 因此这些代码在内存中的位置是固定的
    	 - 在程序中使用静态库中的函数的时候, 不需要额外加载, 直接调用即可
    	 - 效率高
    */
    
    /*
    	动态库: libcalc.so
    	 - 链接命令: gcc main.c -o app -L ./ -l calc
    	 - 动态库是不会被打包到可执行程序中, 只是将动态库名和其中的函数进行了记录, 动态库没被加载到内存中
    	 - app在运行过程中, 要调用动态中的函数的时候, 动态库才会被加载到内存
    	 	- 加载动态需要使用系统的动态链接器, 动态链接器按照以下路径去搜索:(优先级)
    	 		- DT_RPATH段 -> 无法修改, 跳过
    	 		- 环境变量LD_LIBRARY_PATH
    	 		- /etc/ld.so.cache 文件
    	 		- /lib/, /usr/lib目录
    	 		- 在以上文件/目录中动态链接器都没有找到动态库的存储目录, 提示这个库找不到无法加载
    	 - 多个不相关的程序是可以共同使用加载到内存中的动态库的
    */
    

    解决动态库找不到的错误

# 方式1: 修改环境变量: LD_LIBRARY_PATH
# 	环境变量取值: $LD_LIBRARY_PATH
#   1. 临时设置: (关闭当前终端或在其他终端中都是无效的)
#		找到到动态库的绝对路径:  /home/robin/Linux/3Day/mytest
# 		将找到的路径加入环境变量中: (在当前终端中执行)
#			export LD_LIBRARY_PATH=/home/lwh/Desktop/study/03dynamicLib/main:$LD_LIBRARY_PATH
#	2. 永久设置:
# 		将第一步中的export写入配置文件
#			用户级别: ~/.bashrc
#			系统级别: /etc/profile
#		将 export LD_LIBRARY_PATH=/home/robin/Linux/3Day/mytest:$LD_LIBRARY_PATH
# 		写入上述任一文件中
#		重新加载设置: (不执行以下命令, 可以关闭当前终端在重新打开)
# 		. ~/.bahsrc      . == source
#		. /etc/profile

# 方式2, 找/etc/ld.so.conf文件
#		1. 将动态库的绝对路径写入到这个文件的空行中, 保存退出
#		2. /etc/ld.so.conf 更新到 -> /etc/ld.so.cache 文件
#			sudo ldconfig

# 方式3: 将动态库移动/拷贝 到 /lib, /usr/lib任意目录中都可以

1. 生成libXXX.so的Makefile

# 根据 *.c 和 .h
# 生成动态库 libcalc.so

#---------------------------------------------------------
# 工程名 / 库名 # 生成静态库 libcalc.so  计算简单的加减乘除
PROJECT     = libcalc
OUTNAME     = $(PROJECT).so
#---------------------------------------------------------
# 头文件目录
INCLUDEPATH = -I./include/
#---------------------------------------------------------
CC          = gcc
LD          = gcc
#---------------------------------
# 编译依赖项
CFlag       = -w -g -ggdb -fshort-wchar -std=c11 -fPIC $(INCLUDEPATH)
#---------------------------------
# 链接依赖项
LIBEXPORT   = -fvisibility=hidden
.DEFINES    = -DTRACE_LOG
CxxFlage    = -shared  $(LIBEXPORT) $(.DEFINES)
#---------------------------------------------------------
# .o文件
SrcSuf      = c
ObjSuf      = o
LibSuf      = so

OBJFILES    = ./add.$(ObjSuf)\
              ./div.$(ObjSuf)\
              ./mult.$(ObjSuf)\
              ./sub.$(ObjSuf)\
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf)


all:  $(PROJECT) clean

$(PROJECT):$(OBJFILES)
	@echo "Linking $(OUTNAME) start..."
	$(LD) $(OBJFILES) -o $(OUTNAME) $(CxxFlage) -ldl
	@echo "Linking $(OUTNAME) end"

clean:
	@echo "Cleaning $(PROJECT) project files"
	@rm -f $(OBJFILES) core

.$(SrcSuf).$(ObjSuf):
	@echo "Compiling $(PROJECT) $<"
	$(CC) $(CFlag) -c $< -o $@  

2. 使用libXXX.so的Makefile

# 根据main.c libcalc.so head.h 
# 生成可执行文件 execute
#---------------------------------------------------------
# 工程名  计算简单的加减乘除
PROJECT     = execute
#---------------------------------------------------------
# .o文件
SrcSuf      = c
ObjSuf      = o
LibSuf      = so

OBJFILES    = ./main.$(ObjSuf)
#---------------------------------------------------------
#依赖的静态库库
#LIBS        = -l../libcalc.a
#依赖的动态库
LIBS2       = -l calc
#---------------------------------------------------------
CC          = gcc

# 头文件目录
INCLUDEPATH = -I../include/
#依赖库所在的目录
LIBPATH     = -L../

# 编译依赖项
CFlag       = -w -g -ggdb -fshort-wchar -std=c11  $(INCLUDEPATH)
# 链接依赖项
CxxFlage    = $(INCLUDEPATH) $(LIBPATH)
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf)

all:  $(PROJECT) clean

$(PROJECT):$(OBJFILES)
	@echo "creating $(PROJECT) start..."
	$(CC) $(OBJFILES) -o $(PROJECT) $(CxxFlage) $(LIBS2) 
	@echo "creating $(PROJECT) end"


clean:
	@echo "Cleaning $(PROJECT) project files"
	@rm -f $(OBJFILES) core

.$(SrcSuf).$(ObjSuf):
	@echo "Compiling $(PROJECT) $<"
	$(CC) $(CFlag) -c $< -o $@

2.4 对比

  • 静态库

    • 优点:
    • 静态库被打包到应用程序中加载速度快
      • 发布程序无需提供静态库,移植方便
    • 缺点:
    • 消耗系统资源,浪费内存
      • 更新、部署、发布麻烦。

在这里插入图片描述

  • 动态库

    • 优点:

      • 可实现进程间资源共享
      • 程序升级简单
      • 程序猿可以控制何时加载动态库
    • 缺点:

      • 加载速度比静态库慢
      • 发布程序需要提供依赖的动态库

在这里插入图片描述

vim插件-vimplus

# vim插件
https://github.com/chxuan/vimplus

猜你喜欢

转载自blog.csdn.net/liangwenhao1108/article/details/107433074
今日推荐