admin管理员组

文章数量:1487745

【Linux】————动静态库

一.库的定义

什么是库,在windows平台和linux平台下都大量存在着库。

本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

由于windows和linux的本质不同,因此二者库的二进制是不兼容的。

本文仅限于介绍linux下的库。

二.库的种类

1. 在windows中

.dll 动态库

.lib 静态库

2. 在linux中

.so 动态库

.a 静态库

二者的不同点在于代码被载入的时刻不同。 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。 共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

三.库存在的意义

库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。

现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

四.库文件是如何产生的在linux下

静态库的后缀是.a,它的产生分两步:

Step 1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表

Step 2.ar命令将很多.o转换成.a,成文静态库

动态库的后缀是.so,它由gcc加特定参数编译产生。

五.程序在不同环境下运行时怎么寻找库

1. Linux系统默认到哪里找命令,如果命令不在那里,该怎么设置?

(1) Linux在运行一条命令时,默认会到 /bin , /sbin ,, /usr/bin, /usr/sbin目录下去找;如果找不到则报command not found (2)如果命令存放在其他路径下,我们可以通过export PATH导出,不过这样只是临时生效; (3) 如果想让所有用户生效,则修改/etc/profile,里面通过export PATH导出路径; (4) 在PC上如果只想对本用户生效,则修改~/.bash_profile

2. LInux程序在运行时到哪里找动态库, 如果动态库不在那里,该怎么设置?

(1)若是在开发板程序运行时:修改/etc/profile,export LD_LIBRARY_PATH添加库的其他存放路径。

(2)若是在片PC上程序运行时:动态库默认路径为/usr/lib和/lib,可在/etc/ld.so.conf中添加指定动态库搜索路径,通过LD_LIBRARY_PATH(LD_LIBRARY_PATH是Linux环境变量名,该环境变量主要用于指定查找共享库(动态链接库)时除了默认路径之外的其他路径)命令指定,假如现在需要在已有的环境变量上添加新的路径名,则采用如下方式:

LD_LIBRARY_PATH=NEWDIRS:$LD_LIBRARY_PATH.(newdirs是新的路径串)。

注:在linux下可以用export命令来设置这个值,比如 在linux终端下输入:export LD_LIBRARY_PATH=/opt/au1200_rm/build_tools/bin: $LD_LIBRARY_PATH: 然后再输入:export,即会显示是否设置正确 export方式在重启后失效,所以也可以用 vim /etc/bashrc ,修改其中的LD_LIBRARY_PATH变量。

六.如何知道一个可执行程序依赖哪些库

ldd命令可以查看一个可执行程序依赖的共享库

七:重点

1.静态库:

1.1、怎么做静态库:

在Linux环境下,通常使用GCC(GNU Compiler Collection)编译器来编译源代码,并使用ar(archiver)工具来创建静态库

  1. 编写源代码:首先,你需要有一些源代码文件,比如 x.c ,y.c ,z.c
  2. 编译源代码为对象文件:使用GCC编译器将源代码编译为目标文件(.o文件)。
  3. 创建静态库:使用 ar工具将对象文件打包成静态库。

头文件是一个手册提供函数的声明,告诉用户怎么用;.o提供实现,我们只需要补上一个main函数,调用头文件提供的方法,然后和.o进行链接,就能形成可执行。

头文件是一个手册提供函数的声明,告诉用户怎么用;.o提供实现,我们只需要补上一个main函数,调用头文件提供的方法,然后和.o进行链接,就能形成可执行。

mymath.h

代码语言:javascript代码运行次数:0运行复制
#pragma once // 防止头文件重复包含
 
#include <stdio.h>
 
int Add(int x,int y);

mymath.c

代码语言:javascript代码运行次数:0运行复制
#include "mymath.h"
 
int Add(int x,int y)
{
  return x + y;
}

mystdio.h

代码语言:javascript代码运行次数:0运行复制
#pragma once 
 
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
#define LINE_SIZE 1024
#define FLUSH_NOW  1
#define FLUSH_LINE 2
#define FLUSH_FULL 4
 
typedef struct _myFILE  
{
  unsigned int flags;
  int fileno;
  // 缓冲区
  char cache[LINE_SIZE];
  int cap;// 容量
  int pos;// 下次写入的位置
}myFILE;
 
myFILE* my_fopen(const char* path,const char* flag);
void my_fflush(myFILE* fp);
ssize_t my_fwrite(myFILE* fp,const char* data,int len);
void my_fclose(myFILE* fp);

mystdio.c

代码语言:javascript代码运行次数:0运行复制
#include "mystdio.h"
 
myFILE* my_fopen(const char* path,const char* flag)
{
  int flag1 = 0;
  int iscreate = 0;
  mode_t mode = 0666;
  if(strcmp(flag,"r") == 0)
  {
    flag1 = O_RDONLY;
  }
  else if(strcmp(flag,"w") == 0)
  {
    flag1 = (O_WRONLY | O_CREAT | O_TRUNC);
    iscreate = 1;
  }
  else if(strcmp(flag,"a") == 0)
  {
    flag1 = (O_WRONLY | O_CREAT | O_APPEND);
    iscreate = 1;
  }
  else 
  {}
 
  int fd = 0;
  if(iscreate)
    fd = open(path,flag1,mode);
  else 
    fd = open(path,flag1);
  
  if(fd < 0) return NULL;
  
  myFILE* fp = (myFILE*)malloc(sizeof(myFILE));
  if(fp == NULL) return NULL;
 
  fp->fileno = fd;
  fp->flags = FLUSH_LINE;
 
  fp->cap = LINE_SIZE;
  fp->pos = 0;
 
  return fp;
}
void my_fflush(myFILE* fp)
{
  write(fp->fileno,fp->cache,fp->pos);
  fp->pos = 0;
}
ssize_t my_fwrite(myFILE* fp,const char* data,int len)
{
  // 写入的本质是拷贝,条件允许就刷新
  memcpy(fp->cache + fp->pos ,data,len);// 考虑扩容与越界问题
  fp->pos += len;
  
  if((fp->flags&FLUSH_LINE) && fp->cache[fp->pos-1] == '\n')
  {
      my_fflush(fp);
  }
  return len; 
}
void my_fclose(myFILE* fp)
{
  my_fflush(fp);
  close(fp->fileno);
  free(fp);
}

main.c

代码语言:javascript代码运行次数:0运行复制
#include "mymath.h"
#include "mystdio.h"
#include <string.h>
#include <stdio.h>
 
int main()
{
  int a = 10;
  int b = 20;
  
  printf("%d + %d  = %d\n",a,b,myAdd(a,b));
 
  myFILE* fp = my_fopen("log.txt","w");
  if(fp == NULL) return 1;
 
  const char* message = "这是我写的...\n";
 
  my_fwrite(fp,message,strlen(message));
  my_fclose(fp);
  return 0;
}

编译并执行程序

将.c文件(源文件)编译成.o文件(目标文件) [ -c选项告诉GCC只编译和汇编,但不链接]

使用.h 文件和.o 文件编译main.c程序

gcc main.c 只编译了main.c文件,并没有包含对mymath.o 和 mystdio.h 的链接操作,因为main.c 依赖于mymath.h 和 mystdio.h 中声明的函数,因此仅编译main.c是不够的。

解决办法一:

将.o文件 和.c文件一起编译链接。

解决办法二:

将main.o也编译成.o文件

解决方法三:通过ar指令将所有.o文件打包:

ar -rc libmyc.a *.o # 将所有.o文件打包成libmyc.a文件

1.2、怎么使用静态库
  • 方式一:直接使用打包的文件

为了更好的使用静态库,我们把前面打包的文件拷贝到另外的目录进行操作。

这里我们创建一个other目录并cd进去

然后将之前形成的libmystdio.a文件以及两个.h头文件拷贝过来拷贝过来

这样就可以了

  • 方式二:将打包的文件拷贝到系统库中(严重不推荐)

通过这几步操作就将这些文件拷贝到啦目录底下

但是直接使用gcc编译还是会报错,因为该方法的实现是我们自己写的,gcc/g++不认识,所以直接编译会报错。

在gcc编译.c文件之后需要加参数,-l libmyc.a,且需要去掉lib和.a,因此正确的命令是gcc main.c -lmyc (-l后面可以加空格也可以不加空格)

第二种方式不推荐,因此演示完之后最好将拷贝的文件给删除掉。

方式三:通过命令链接静态库

为什么不能直接使用 gcc main.c myc.a 因为告诉了gcc/g++编译器,但是没有告诉操作系统!!!

使用静态库:在编译其他程序时,可以通过-I(指定用户自定义头文件搜索路径) -L(指定用户自定义库文件搜索路径)-l(执行确定的第三方库名称,去掉前缀lib和后缀.a)选项来链接静态库

最后:

十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:

1.一个冷知识: 屏蔽力是一个人最顶级的能力,任何消耗你的人和事,多看一眼都是你的不对。

2.你不用变得很外向,内向挺好的,但需要你发言的时候,一定要勇敢。 正所谓:君子可内敛不可懦弱,面不公可起而论之。

3.成年人的世界,只筛选,不教育。

4.自律不是6点起床,7点准时学习,而是不管别人怎么说怎么看,你也会坚持去做,绝不打乱自己的节奏,是一种自我的恒心。

5.你开始炫耀自己,往往都是灾难的开始,就像老子在《道德经》里写到:光而不耀,静水流深。

最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)

愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚菜鸟逐渐成为大佬。加油,为自己点赞!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2024-10-22,如有侵权请联系 cloudcommunity@tencent 删除打包linux编译程序函数

本文标签: Linux动静态库