我的编程空间,编程开发者的网络收藏夹
学习永远不晚

什么是C语言动态库

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

什么是C语言动态库

这篇文章主要讲解了“什么是C语言动态库”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是C语言动态库”吧!

什么? 这里竟然躺着一个高性能 base64 库?

我们以这个 repo 为例: https:// github.com/aklomp/base6 4 . 这是一个 C 编写的 Base64 编码/解码库, 而且支持SIMD.

可以简单运行下这个库的 benchmark:

karminski@router02:/data/works/base64$ make clean && SSSE3_CFLAGS=-mssse3 AVX2_CFLAGS=-mavx2 make && make -C test ... Testing with buffer size 100 KB, fastest of 10 * 100 AVX2    encode  12718.47 MB/sec AVX2    decode  14542.81 MB/sec plain   encode  3657.40 MB/sec plain   decode  3433.23 MB/sec SSSE3   encode  7269.55 MB/sec SSSE3   decode  8173.10 MB/sec ...

我的 CPU 是 Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz, 可以看到CPU如果支持 AVX2 的话, 可以达到 12GB/s 以上, 这个性能非常强悍, 甚至连普通的SSD都跟不上了.

我们需要的第一步是把这个 repo 编译为动态库. 但是这个 repo 并没有提供动态库的编译选项, 所以我们魔改下这个项目的 Makefile.

CFLAGS += -std=c99 -O3 -Wall -Wextra -pedantic  # Set OBJCOPY if not defined by environment: OBJCOPY ?= objcopy  OBJS = \   lib/arch/avx2/codec.o \   lib/arch/generic/codec.o \   lib/arch/neon32/codec.o \   lib/arch/neon64/codec.o \   lib/arch/ssse3/codec.o \   lib/arch/sse41/codec.o \   lib/arch/sse42/codec.o \   lib/arch/avx/codec.o \   lib/lib.o \   lib/codec_choose.o \   lib/tables/tables.o  SOOBJS = \   lib/arch/avx2/codec.so \   lib/arch/generic/codec.so \   lib/arch/neon32/codec.so \   lib/arch/neon64/codec.so \   lib/arch/ssse3/codec.so \   lib/arch/sse41/codec.so \   lib/arch/sse42/codec.so \   lib/arch/avx/codec.so \   lib/lib.so \   lib/codec_choose.so \   lib/tables/tables.so  HAVE_AVX2   = 0 HAVE_NEON32 = 0 HAVE_NEON64 = 0 HAVE_SSSE3  = 0 HAVE_SSE41  = 0 HAVE_SSE42  = 0 HAVE_AVX    = 0  # The user should supply compiler flags for the codecs they want to build. # Check which codecs we're going to include: ifdef AVX2_CFLAGS   HAVE_AVX2 = 1 endif ifdef NEON32_CFLAGS   HAVE_NEON32 = 1 endif ifdef NEON64_CFLAGS   HAVE_NEON64 = 1 endif ifdef SSSE3_CFLAGS   HAVE_SSSE3 = 1 endif ifdef SSE41_CFLAGS   HAVE_SSE41 = 1 endif ifdef SSE42_CFLAGS   HAVE_SSE42 = 1 endif ifdef AVX_CFLAGS   HAVE_AVX = 1 endif ifdef OPENMP   CFLAGS += -fopenmp endif   .PHONY: all analyze clean  all: bin/base64 lib/libbase64.o lib/libbase64.so  bin/base64: bin/base64.o lib/libbase64.o lib/libbase64.so     $(CC) $(CFLAGS) -o $@ $^  lib/libbase64.o: $(OBJS)     $(LD) -r -o $@ $^     $(OBJCOPY) --keep-global-symbols=lib/exports.txt $@  lib/libbase64.so: $(SOOBJS)     $(LD) -shared -fPIC -o $@ $^     $(OBJCOPY) --keep-global-symbols=lib/exports.txt $@  lib/config.h:     @echo "#define HAVE_AVX2   $(HAVE_AVX2)"    > $@     @echo "#define HAVE_NEON32 $(HAVE_NEON32)" >> $@     @echo "#define HAVE_NEON64 $(HAVE_NEON64)" >> $@     @echo "#define HAVE_SSSE3  $(HAVE_SSSE3)"  >> $@     @echo "#define HAVE_SSE41  $(HAVE_SSE41)"  >> $@     @echo "#define HAVE_SSE42  $(HAVE_SSE42)"  >> $@     @echo "#define HAVE_AVX    $(HAVE_AVX)"    >> $@  $(OBJS): lib/config.h  $(SOOBJS): lib/config.h  # o lib/arch/avx2/codec.o:   CFLAGS += $(AVX2_CFLAGS) lib/arch/neon32/codec.o: CFLAGS += $(NEON32_CFLAGS) lib/arch/neon64/codec.o: CFLAGS += $(NEON64_CFLAGS) lib/arch/ssse3/codec.o:  CFLAGS += $(SSSE3_CFLAGS) lib/arch/sse41/codec.o:  CFLAGS += $(SSE41_CFLAGS) lib/arch/sse42/codec.o:  CFLAGS += $(SSE42_CFLAGS) lib/arch/avx/codec.o:    CFLAGS += $(AVX_CFLAGS) # so lib/arch/avx2/codec.so:   CFLAGS += $(AVX2_CFLAGS) lib/arch/neon32/codec.so: CFLAGS += $(NEON32_CFLAGS) lib/arch/neon64/codec.so: CFLAGS += $(NEON64_CFLAGS) lib/arch/ssse3/codec.so:  CFLAGS += $(SSSE3_CFLAGS) lib/arch/sse41/codec.so:  CFLAGS += $(SSE41_CFLAGS) lib/arch/sse42/codec.so:  CFLAGS += $(SSE42_CFLAGS) lib/arch/avx/codec.so:    CFLAGS += $(AVX_CFLAGS)  %.o: %.c     $(CC) $(CFLAGS) -o $@ -c $<  %.so: %.c     $(CC) $(CFLAGS) -shared -fPIC -o $@ -c $<  analyze: clean     scan-build --use-analyzer=`which clang` --status-bugs make  clean:     rm -f bin/base64 bin/base64.o lib/libbase64.o lib/libbase64.so lib/config.h $(OBJS)

看不懂没关系, Makefile 是如此的复杂, 我也看不懂, 仅仅是凭着感觉修改的, 然后他就恰好能运行了... 注意 Makefile 的缩进一定要用 "\t", 否则不符合语法会报错.

然后我们进行编译:

AVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.so

这样我们就得到了libbase64.so 动态库 (在 lib 里面). 这里还顺便开启了各种 SIMD 选项. 如果不需要的话可以关闭.

魔改开始

当然这只是魔法, 不是炼金术, 所以是需要付出努力的, 我们要手动实现动态库的桥接, 首先我们需要查看我们要调用的函数需要什么参数. 这两个定义很简单, 我们需要传入:

const char *class="lazy" data-src size_t class="lazy" data-srclen char *out size_t *outlen int flags
void base64_encode(const char *class="lazy" data-src, size_t class="lazy" data-srclen, char *out, size_t *outlen, int flags); int  base64_decode(const char *class="lazy" data-src, size_t class="lazy" data-srclen, char *out, size_t *outlen, int flags);

然后我们就可以开始编写 ffi 桥接程序了. 首先把需要的库全都包含进来, 注意, 多用 local 没坏处, 使用 local 可以有效从局部查询, 避免低效的全局查询. 甚至其他包中的函数都可以 local 一下来提升性能.

动态库的话用专用的 ffi.load 来引用.

然后定义一个 _M 用来包裹我们的库. 这里跟 JavaScript 很像, JavaScript 在浏览器里有 window, Lua 有 _G. 我们要尽可能避免封装好的库直接扔给全局, 因此封装起来是个好办法.

-- init local ffi        = require "ffi" local floor      = math.floor local ffi_new    = ffi.new local ffi_str    = ffi.string local ffi_typeof = ffi.typeof local C          = ffi.C local libbase64  = ffi.load("./libbase64.so") -- change this path when needed.  local _M = { _VERSION = '0.0.1' }

然后是用 ffi.cdef 声明 ABI 接口, 这里更简单, 直接把源代码的头文件中的函数声明拷过来就完事了:

-- cdef ffi.cdef[[ void base64_encode(const uint8_t *class="lazy" data-src, size_t class="lazy" data-srclen, uint8_t *out, size_t *outlen, size_t flags); int  base64_decode(const uint8_t *class="lazy" data-src, size_t class="lazy" data-srclen, uint8_t *out, size_t *outlen, size_t flags); ]]

接下来是最重要的类型转换:

-- define types local uint8t    = ffi_typeof("uint8_t[?]") -- uint8_t * local psizet    = ffi_typeof("size_t[1]")  -- size_t *  -- package function function _M.base64_encode(class="lazy" data-src, flags)     local dlen   = floor((#class="lazy" data-src * 8 + 4) / 6)     local out    = ffi_new(uint8t, dlen)     local outlen = ffi_new(psizet, 1)     libbase64.base64_encode(class="lazy" data-src, #class="lazy" data-src, out, outlen, flags)     return ffi_str(out, outlen[0])  end   function _M.base64_decode(class="lazy" data-src, flags)     local dlen   = floor((#class="lazy" data-src + 1) * 6 / 8)     local out    = ffi_new(uint8t, dlen)     local outlen = ffi_new(psizet, 1)     libbase64.base64_decode(class="lazy" data-src, #class="lazy" data-src, out, outlen, flags)     return ffi_str(out, outlen[0]) end

我们用 ffi_typeof 来定义需要映射的数据类型, 然后用 ffi_new 来将其实例化, 分配内存空间. 具体来讲:

我们定义了2种数据类型, 其中, local uint8t = ffi_typeof("uint8_t[?]") 类型用来传输字符串, 后面的问号是给 local out = ffi_new(uint8t, dlen) 中的 ffi_new 函数准备的, 它的第二个参数可以指定实例化该数据类型时的长度. 这样我们就得到了一个空的字符串数组, 用来装 C 函数返回的结果. 这里的 dlen 计算出了源字符串 base64 encode 之后的长度, 分配该长度即可.

同样, local psizet = ffi_typeof("size_t[1]") 指定了一个 size_t * 类型. C 语言里面数组就是指针, 即 size_t[0] 与 site_t* 是等价的. 因此我们分只有一个元素的 size_t 数组就得到了指向 size_t 类型的指针. 然后在 local outlen = ffi_new(psizet, 1) 的时候后面的参数写的也是1, 不过这里写什么已经无所谓了, 它只是不支持传进去空, 所以我们相当于传了个 placeholder.

在使用这个值的时候, 我们也是按照数组的模式去使用的: return ffi_str(out, outlen[0]) .

需要注意的是, 一定要将 require "ffi" 以及 ffi.load 放在代码最底层, 否则会出现 table overflow 的情况.

最后, 这个文件是这样子的:

--[[       ffi-base64.lua          @version    20201228:1     @author     karminski <code.karminski@outlook.com>  ]]--  -- init local ffi        = require "ffi" local floor      = math.floor local ffi_new    = ffi.new local ffi_str    = ffi.string local ffi_typeof = ffi.typeof local C          = ffi.C local libbase64  = ffi.load("./libbase64.so") -- change this path when needed.  local _M = { _VERSION = '0.0.1' }   -- cdef ffi.cdef[[ void base64_encode(const uint8_t *class="lazy" data-src, size_t class="lazy" data-srclen, uint8_t *out, size_t *outlen, size_t flags); int  base64_decode(const uint8_t *class="lazy" data-src, size_t class="lazy" data-srclen, uint8_t *out, size_t *outlen, size_t flags); ]]  -- define types local uint8t    = ffi_typeof("uint8_t[?]") -- uint8_t * local psizet    = ffi_typeof("size_t[1]")  -- size_t *  -- package function function _M.base64_encode(class="lazy" data-src, flags)     local dlen   = floor((#class="lazy" data-src * 8 + 4) / 6)     local out    = ffi_new(uint8t, dlen)     local outlen = ffi_new(psizet, 1)     libbase64.base64_encode(class="lazy" data-src, #class="lazy" data-src, out, outlen, flags)     return ffi_str(out, outlen[0])  end   function _M.base64_decode(class="lazy" data-src, flags)     local dlen   = floor((#class="lazy" data-src + 1) * 6 / 8)     local out    = ffi_new(uint8t, dlen)     local outlen = ffi_new(psizet, 1)     libbase64.base64_decode(class="lazy" data-src, #class="lazy" data-src, out, outlen, flags)     return ffi_str(out, outlen[0]) end   return _M

好了, 大功告成, 我们写个 demo 调用一下试试:

-- main.lua local ffi_base64 = require "ffi-base64"   local target = "https://example.com"  local r = ffi_base64.base64_encode(target, 0) print("base64 encode result: \n"..r)  local r = ffi_base64.base64_decode(r, 0) print("base64 decode result: \n"..r)
root@router02:/data/works/libbase64-ffi# luajit -v LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2020 Mike Pall. https://luajit.org/ root@router02:/data/works/libbase64-ffi# luajit ./main.lua  base64 encode result:  aHR0cHM6Ly9leGFtcGxlLmNvbQ== base64 decode result:  https://example.com

感谢各位的阅读,以上就是“什么是C语言动态库”的内容了,经过本文的学习后,相信大家对什么是C语言动态库这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

什么是C语言动态库

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

c语言生成动态库的方法是什么

在C语言中,生成动态库的方法一般是通过编译链接的方式来实现的。下面是一般的步骤:编写源代码文件:首先编写需要生成动态库的源代码文件,通常以.c为扩展名。编译源文件:使用编译器将源代码文件编译成目标文件(.o文件),例如使用gcc编译器可以使
c语言生成动态库的方法是什么
2024-03-02

c语言动态规划算法是什么

C语言动态规划算法是一种用于解决优化问题的算法。它通过将问题划分为子问题,并保存子问题的解来避免重复计算,从而提高算法的效率。动态规划算法通常使用一个数组来保存子问题的解,这个数组称为“动态规划表”。算法的核心思想是通过填充动态规划表来逐步
2023-08-18

C语言动态内存管理的方法是什么

本文小编为大家详细介绍“C语言动态内存管理的方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“C语言动态内存管理的方法是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.为什么需要动态内存分配关于这个
2023-06-29

C语言的动态内存管理是什么意思

这篇文章将为大家详细讲解有关C语言的动态内存管理是什么意思,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、动态内存分配(1)用malloc类的函数分配内存;(2)用这些内存支持应用程序;(3)用free
2023-06-29

c语言动态库怎么创建和使用

要创建一个动态库(也称为共享库),通常需要遵循以下步骤:编写库的源代码文件,通常是以.c为扩展名的文件,包含库中所需的函数和数据结构的实现代码。编译源代码文件并链接为一个共享库。在编译时,需要使用-shared选项将生成的目标文件链接为一个
c语言动态库怎么创建和使用
2024-03-02

C语言中的状态机是什么

本篇内容主要讲解“C语言中的状态机是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言中的状态机是什么”吧! 前言本文不是关于软件状态机的最佳设计分解实践的教程。我将重点关注状态机代码和简
2023-06-15

C++调用动态库和Python调用C++动态库的方法是什么

这篇文章主要介绍“C++调用动态库和Python调用C++动态库的方法是什么”,在日常操作中,相信很多人在C++调用动态库和Python调用C++动态库的方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答
2023-07-05

C语言静态与动态通讯录的实现方法是什么

这篇文章主要讲解了“C语言静态与动态通讯录的实现方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言静态与动态通讯录的实现方法是什么”吧!静态通讯录在我们学习完C语言的结构体、指针
2023-06-25

go语言是不是动态语言

这篇文章主要介绍了go语言是不是动态语言的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇go语言是不是动态语言文章都会有所收获,下面我们一起来看看吧。go不是动态语言。go语言是Google开发的一种静态强类型、
2023-07-04

c语言是什么语言

c语言作为一种通用、过程式编程语言,自诞生以来一直是计算机领域最流行的语言之一。其简洁高效、跨平台、强大的控制能力、丰富的库函数和可扩展性等特点,使其广泛应用于系统软件开发、嵌入式系统开发、游戏开发、网络编程等多个领域。通过学习c语言,可以
c语言是什么语言
2024-03-14

C#的动态类型语法是什么

这篇文章主要介绍了C#的动态类型语法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#的动态类型语法是什么文章都会有所收获,下面我们一起来看看吧。动态(Dynamic)类型您可以存储任何类型的值在动态数据
2023-06-17

go语言是动态语言吗

go不是动态语言。go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言,其变量(variable)是有明确类型的,编译器也会检查变量类型的正确性;因此在使用变量之前必须声明数据类型需要,语法“var 变量名 变量类型”。
2023-05-14

java动态和静态语言的区别是什么

今天就跟大家聊聊有关java动态和静态语言的区别是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1、动态语言在运行中可以改变结构的语言。例如,新函数、对象、代码可以引进,现有函数
2023-06-15

C++中Cmake的构建静态库和动态库是什么

这篇文章主要为大家展示了“C++中Cmake的构建静态库和动态库是什么”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C++中Cmake的构建静态库和动态库是什么”这篇文章吧。静态库和动态库的区别
2023-06-29

c语言graphics库的用法是什么

C语言中的Graphics库是一个用于图形绘制的库。它提供了一系列函数和数据类型,可以在图形窗口中绘制各种图形、文本和图像。Graphics库的使用步骤如下:引入头文件:在程序中引入graphics.h头文件,该头文件包含了图形库的函数和
2023-10-23

c#是什么语言

c# 是一种面向对象的高级跨平台编程语言,由 microsoft 开发,用于构建桌面、web、移动应用程序和游戏。它采用 c 风格的语法,支持 oop、自动垃圾回收和泛型等功能,并在 windows、macos 和 linux 等操作系统上
c#是什么语言
2024-04-04

C++是什么语言

这篇文章主要介绍“C++是什么语言”,在日常操作中,相信很多人在C++是什么语言问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++是什么语言”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!C++是一种面向
2023-06-17

Go语言:静态还是动态?

go 是一种静态类型语言,但通过使用 interface{} 类型,它支持一些动态特性的元素,允许在运行时动态检查变量类型。这提供了灵活性,但需要谨慎使用以避免运行时错误。Go语言:静态还是动态?Go 编程语言以其静态类型系统而闻名,但它
Go语言:静态还是动态?
2024-04-08

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录