5. 库相关

news/2024/11/14 12:42:38/文章来源:https://www.cnblogs.com/kobayashilin1/p/18302667

5. 库相关

有些时候我们编写的源代码并不需要将他们编译生成可执行程序,而是生成一些静态库动态库提供给第三方使用,下面来讲解在cmake中生成这两类库文件的方法。

5.1 什么是库

本部分介绍创建与使用静态库、动态库,知道静态库与动态库的区别,知道使用的时候如何选择。这里不深入介绍静态库、动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书《程序员的自我修养——链接、装载与库》。

5.1.1 什么是库

库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常

本质上来说,库是一种可执行的二进制格式,可以被操作系统载入内存执行。库有两种:静态库(.a .lib )和动态库(.so .dll)。

这里的静态、动态是指链接类型的区别,分为静态链接方式和动态链接方式。编译的过程如下:

									 ____
源文件 --> 预编译 --> 编译 --> 汇编 --> |链接| --> 可执行文件----.so/.dll	   ^.a/.lib        |______________/

5.1.2 静态库

之所以成为静态库,是因为在链接阶段,会将汇编生成的目标文件.o/.obj与引用到的库一起链接打包到可执行文件(ELF格式或者PE格式,前者 Linux 系统使用,后者 Windows 系统使用)中。因此对应的链接方式称为静态链接。

静态库和汇编生成的目标文件一起链接,可以生出可执行文件,那么静态库必定和 .o/.obj 文件格式相似。所以一个静态库可以看作是一组目标文件(.o/.obj)的集合,静态库可以很大,且方便移植。

静态库的特点:

  • 静态库对函数库的链接是放在编译期完成的
  • 程序在运行时与函数库无瓜葛,方便移植
  • 占用更多的存储空间资源,因为所有相关的目标文件与牵涉到的函数库都被合成为一个可执行文件

静态库生成

在 Linux 下使用 ar 工具,在 Windows 下使用 lib.exe 工具,可以将目标文件压缩在一起,并且对其进行编号和索引,以便查找和检索。创建静态库步骤如下图所示:

clip_image004[4]

  • Linux 下静态库命名规则:lib[your_library_name].alib 为前缀,中间为静态库名,扩展名为 .a(Windows 下后缀名为 .lib

    • 先将源代码编译为 .o 的目标文件。我使用先前的简单自制数学库作为例子(为了方便示例,我这里将头文件和源代码文件放在同一个目录下)

      tree .
      .
      ├── add.cpp
      ├── div.cpp
      ├── head.h
      ├── mul.cpp
      └── sub.cpp0 directories, 5 files
      

      生成目标文件:

      gcc -c -Wall *.cpp
      

      此时生成了各自的同名目标文件:

      tree 
      .
      ├── add.cpp
      ├── add.o
      ├── div.cpp
      ├── div.o
      ├── head.h
      ├── mul.cpp
      ├── mul.o
      ├── sub.cpp
      └── sub.o
      
    • 然后,通过 ar 工具将目标文件打包成 .a 静态库文件

      ar -crv libmymath.a *.o
      a - add.o
      a - div.o
      a - mul.o
      a - sub.o
      

      然后查看文件:其中就有 libmymath.a,也就是我想要的静态库

      ls
      add.cpp  add.o  div.cpp  div.o  head.h  libmymath.a  mul.cpp  mul.o  sub.cpp  sub.o
      

注:Windows 上如果使用 g++,生成静态库的操作是一样的,只是文件后缀有些许不同

静态库使用

此时我的工程目录是这样的(在这里并没有使用 CMakeLists.txt):

tree .
.
├── CMakeLists.txt
├── include
│   └── head.h
├── main.cpp
└── src├── add.cpp├── add.o├── div.cpp├── div.o├── head.h├── libmymath.a├── mul.cpp├── mul.o├── sub.cpp└── sub.o

Linux 下使用静态库,只需要在编译的时候,需要如下操作:

  • 指定静态库的搜索路径-L 选项)、
  • 指定静态库名
    • Linux 下不需要 lib 前缀、不需要 .a 后缀,需要-l 选项,如果是
    • Windows 下使用了 mingw 或者 msys2 环境,则 需要 lib 前缀、不需要 .lib 后缀、 需要 -l 选项。
    • -l 选项后面的参数可以写在一起,也可以留一个空格

并将 main.cpp 中的头文件包含修改正确了:

#include "include/head.h"
...

使用我们刚才生成的静态库:

Linux 环境:

 g++ -Wall -g  main.cpp -L ./src -lmymath -o main

Windows 下使用 mingw/msys2 环境:

# 生成静态库
ar -crv libmymath.lib *.o  
# 使用静态库g++ -Wall -g main.cpp -L ./src -llibmymath -o main.exe

注意:Windows 下 -l 不需要 .lib 后缀,但是需要前面所有的名称,意思就是不能省略静态库前面的 lib

然后在当前目录下就生成了一个可执行文件:main

ls
CMakeLists.txt  include  main  main.cpp  src
./main
9.32 19.4052 -3.04 0.508091

注意:

Windows 下不同工具生成的库文件是不通用的,比如使用 Visual Studio 生成的静态库,g++ 就无法使用,反之亦然。

关于 Windows 下使用 Visual Studio 引入或者生成第三方库,可以参考这篇博客

5.1.3 动态库

通过上面的介绍,我们知道静态库已经可以达到代码复用的目的了,但是为什么还需要动态库呢?在我看来,有三个重要原因:

  • 静态库过于浪费空间,因为它将所有的目标文件和汇编都压缩成了一个库文件,这个文件就可能很大,而且静态库在内存中也会拷贝多份,会导致空间浪费

    clip_image021[4]

  • 由于开源协议的原因,部分框架或者软件不允许免费使用静态库(如Qt),否则要收费;

  • 另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库 liba.lib 更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。

    clip_image023[4]

动态库的特征

  • 动态库把对一些库函数的链接载入推迟到程序运行的时期;
  • 可以实现进程之间的资源共享。(因此动态库也称为共享库,.so 全称为 shared object

Window 与 Linux 执行文件格式不同,在创建动态库的时候有一些差异。

  • 在Windows系统下的执行文件格式是 PE格式 ,动态库需要一个 DllMain 函数做出初始化的入口,通常在导出函数的声明时需要有 _declspec(dllexport) 关键字。

  • Linux 下 gcc/g++ 编译的执行文件默认是 ELF格式不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。

与创建静态库不同的是,不需要打包工具(ar、lib.exe),直接使用编译器即可创建动态库。

动态库生成

动态链接库的名字形式为 lib[your_library_name].so ,前缀是 lib ,后缀名为 .so

  • 针对于实际库文件,每个共享库都有个特殊的名字 soname 。在程序启动后,程序通过这个名字来告诉动态加载器该载入哪个共享库;
  • 在文件系统中,soname 仅是一个链接到实际动态库的链接。对于动态库而言,每个库实际上都有另一个名字给编译器来用。它是一个指向实际库镜像文件的链接文件(lib+soname+.so)。

还是以先前的工程代码为例,删除所有生成的文件,恢复到最初的状态:

tree .
.
├── CMakeLists.txt
├── include
│   └── head.h
├── main.cpp
└── src├── add.cpp├── div.cpp├── head.h├── mul.cpp└── sub.cpp2 directories, 8 files
  • 进入文件夹,生成目标文件,添加编译器选项 -fPIC

    g++ -c -Wall -fPIC *.cpp
    

    -fPIC :创建与地址无关的编译程序(Position Independent Code),是为了能够在多个程序之间共享。

    查看生成后的文件:

    tree .
    .
    ├── add.cpp
    ├── add.o
    ├── div.cpp
    ├── div.o
    ├── head.h
    ├── mul.cpp
    ├── mul.o
    ├── sub.cpp
    └── sub.o0 directories, 9 files
    
  • 然后,生成动态库,要添加编译器选项 -shared

    g++ -Wall -shared -o libMyDynamicMath.so *.o
    

    查看生的文件:已经生成了我们想要的动态库文件 libMyDynamicMath.so

    add.cpp  div.cpp  head.h               mul.cpp  sub.cpp
    add.o    div.o    libMyDynamicMath.so  mul.o    sub.o
    

    上述步骤可以合二为一:

    g++ -Wall -fPIC -shared -o libMyDynamicMath.so *.cpp# Windows 的 MinGW/msys2 环境下:
    g++ -Wall -fPIC -shared -o libMyDynamicMath.dll *.cpp
    

动态库使用

使用方式和静态库类似,如我们要引入动态库,编译一个可执行程序:

Linux 环境下:
g++ -Wall -g main.cpp -L ./src -lMyDynamicMath -o main# Windows 的 MinGW/msys2 环境下:
g++ -Wall -g main.cpp -L ./src -llibMyDynamicMath -o main.exe

这时候我们运行:

./main
./main: error while loading shared libraries: libMyDynamicMath.so: cannot open shared object file: No such file or directory

找不到!感觉要炸了!

找不到动态库

根据编译器的错误提示,可以看出,是找不到 .so 的文件,我们需要进行如下设置:

  • 运行时指定库路径

    LD_LIBRARY_PATH=/home/yuzu/cmake_proj/proj4/src ./main
    
  • 将动态链接库添加到标准库目录,如:usr/lib 或者 usr/local/lib,可能需要超级用户权限

  • 设置 LD_LIBRARY_PATH 添加库所在目录到 LD_LIBRARY_PATH 环境变量中。你可以使用以下命令:

    export LD_LIBRARY_PATH=/path/to/your/library:$LD_LIBRARY_PATH
    
  • 如果安装在其他目录,需要将其添加到 /etc/ld.so.cache 文件中,步骤如下:

    • 编辑 /etc/ld.so.conf 文件,加入库文件所在目录的路径
    • 运行 ldconfig ,该命令会重建 /etc/ld.so.cache 文件

我这里选取第一种方式,结果如下:

image-20231201192237097

而在 Windows 环境下,动态链接库一般和可执行程序放在同一个目录下,Windows 下的 .dll 查找顺序为:

  1. exe 所在目录
  2. Windows 系统目录
  3. 当前目录
  4. PATH 指定的目录

5.2 CMake 与库

上面 5.1 部分,我们知晓了在没有其他构建工具的时候,我们是怎么构建静态库和动态库的,可是 5.1 中的操作如果是在小工程中还好,如果工程文件很多,项目很复杂,那么将是灾难,这时候就需要构建工具出来帮忙了,而 CMake 就可以做的很好。

5.2.1 使用 CMake 制作库文件

我将 main.cpp 单独移动到工程主目录下,现在目录结构如下所示:

tree .
.
├── CMakeLists.txt
├── include
│   └── head.h
├── main.cpp
└── src├── add.cpp├── div.cpp├── head.h├── mul.cpp└── sub.cpp2 directories, 8 files

制作静态库

在 CMake 中,制作静态库采用如下命令:

add_library(库名称 STATIC 源文件1 [源文件2] ...)

在 Linux 中,静态库名字分为三部分:lib+库名字+.a,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。

在 Windows 中虽然库名和 Linux 格式不同,但也只需指定出名字即可。

# 指定cmake最低版本
cmake_minimum_required(VERSION 3.17)# 项目名称
project(CALC)# 指定C++标准
set(CMAKE_CXX_STANDARD 20)# 指定可执行文件目录
set(HOME ${CMAKE_CURRENT_SOURCE_DIR})  
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)# 指定头文件目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)# 搜索指定目录下的源文件,保存文件列表到到变量 MAIN_SRC
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)# 生成静态库
add_library(calc STATIC ${MAIN_SRC})

在程序家目录下,使用 cmake 命令执行:

mkdir build && cd build
cmake ..
make

命令行提示:

[ 20%] Building CXX object CMakeFiles/calc.dir/src/add.cpp.o
[ 40%] Building CXX object CMakeFiles/calc.dir/src/div.cpp.o
[ 60%] Building CXX object CMakeFiles/calc.dir/src/mul.cpp.o
[ 80%] Building CXX object CMakeFiles/calc.dir/src/sub.cpp.o
[100%] Linking CXX static library libcalc.a
[100%] Built target calc

此时查看 build 目录,就生成了我们的静态库 libcalc.a

制作动态库

在cmake中,如果要制作动态库,需要使用的命令如下:

add_library(库名称 SHARED 源文件1 [源文件2] ...) 

在Linux中,动态库名字分为三部分:lib+库名字+.so,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。

在Windows中虽然库名和Linux格式不同,但也只需指定出名字即可。

最终的 CMakeLists.txt 文件:

# 指定cmake最低版本
cmake_minimum_required(VERSION 3.17)# 项目名称
project(CALC)# 指定C++标准
set(CMAKE_CXX_STANDARD 20)# 指定可执行文件目录
set(HOME ${CMAKE_CURRENT_SOURCE_DIR})  
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)# 指定头文件目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)# 搜索指定目录下的源文件,保存文件列表到到变量 MAIN_SRC
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)# 生成动态库
add_library(calc SHARED ${MAIN_SRC})

和上面一样运行 cmake 命令,就能在 build 目录下得到想要的 .so 文件

CMakeCache.txt  Makefile             compile_commands.json  libcalc.so
CMakeFiles      cmake_install.cmake  libcalc.a

这里的 libcalc.so 是有可执行权限的,而静态库 .a 没有可执行权限

如何使用生成的库

需要发布两部分数据:

  • 需要 include 目录中的头文件
  • 需要生成的动态库 / 静态库:功能类似于 .cpp 的源代码,不过是二进制格式的

指定输出路径

指定动态库生成目录:[[depricated]]

对于生成的库文件来说和可执行程序一样都可以指定输出路径。由于在 Linux 下生成的动态库默认是有执行权限的,所以可以按照生成可执行程序的方式去指定它生成的目录:

cmake_minimum_required(VERSION 3.17)
project(CALC_LIB)
set(CMAKE_CXX_STANDARD 17)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)# 指定动态库生成目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})

这种方式来说,其实就是通过set命令给 EXECUTABLE_OUTPUT_PATH 宏设置了一个路径,这个路径就是可执行文件生成的路径。

都适用的库生成方式

即既可以指定静态库目录,也可以指定动态库生成目录,(只要我们想)可以生成多个:

cmake_minimum_required(VERSION 3.17)
project(CALC_LIB)
set(CMAKE_CXX_STANDARD 11)set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)# 指定 静态库/动态库 生成目录
set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)add_library(calcso SHARED ${SRC_LIST})
add_library(calcst STATIC ${SRC_LIST})

build 目录下执行 cmake 和 make 命令,然后查看 lib 目录文件:

tree
.
├── libcalcso.so
└── libcalcst.a0 directories, 2 files

我这里生成了两个文件,一个静态库文件 libcalcst.a 和一个动态库文件 libcalcso.so

5.2.2 使用已有库文件

目录结构:

tree
.
├── CMakeLists.txt
├── bin
├── include
│   └── head.h
├── lib
│   ├── libcalcso.so
│   └── libcalcst.a
├── main.cpp
└── src├── add.cpp├── div.cpp├── mul.cpp└── sub.cpp4 directories, 9 files

可以看到,此时 lib 目录下有两个库文件:libcalcso.solibcalcst.a

链接静态库

在 CMake 中,链接静态库命令如下:

link_libraries(<static_lib> [<static_lib> ...])
  • 参数1:指定要链接的静态库名称
    • 可以是全名:libxxx.a
    • 也可以是掐头去尾后的名称:xxx
  • 参数2-N:要链接的其他静态库的名称

注意

如果不是系统提供的静态库,是自己写的或者第三方的库,则可能出现静态库找不到的情况,此时要指定静态库的路径

link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
  • 如果有多个路径,那么路径和路径之间使用空格进行分隔;

  • link_directories 既可以指定静态库路径,又可指定动态库路径

修改后的 CMakeLists.txt 文件:

cmake_minimum_required(VERSION 3.17)
project(CALC_LIB)
set(CMAKE_CXX_STANDARD 11)set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)# 指定链接静态库的目录
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库:指定需要的静态库名称
link_libraries(calcst)add_executable(main ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)

在 build 目录下运行 cmake ..make ,然后就生成了我们想要的可执行文件 main:

./
├── CMakeLists.txt
├── bin/
│   └── main*
......

链接动态库

在程序编写过程中,除了在项目中引入静态库,很多时候也会使用一些标准的或者第三方提供的动态库。动态库是可以被进程之间共享的。

在 CMake 中链接动态库的命令 target_link_libraries :

target_link_libraries(<target> <PRIVATE|PUBLIC|INTERFACE> <item>... [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)

target:指定要加载动态库的文件的名字

  • 该文件可能是一个源文件
  • 该文件可能是一个动态库文件
  • 该文件可能是一个可执行文件

PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为 PUBLIC

  • 如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可

  • 动态库的链接具有传递性,如果动态库 A 链接了动态库 B、C,动态库 D 链接了动态库 A,此时动态库 D 相当于也链接了动态库 B、C,并可以使用动态库 B、C 中定义的方法。

    target_link_libraries(A B C)
    target_link_libraries(D A)
    
  • PUBLIC :在 public 后面的库会被 link 到前面的 target 中,并且里面的符号也会被导出,提供给第三方使用;

  • PRIVATE:在 private 后面的库仅被 link 到前面的 target 中,并且终结掉,第三方不能感知我们具体调了什么库;

  • INTERFACE:在 interface 后面引入的库不会被链接到前面的 target 中,只会导出符号。

链接系统动态库

动态库的链接和静态库是完全不同的:

  • 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了;
  • 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存;

因此,在 CMake 中指定要链接的动态库的时候,应该将命令写到生成了可执行文件之后

cmake_minimum_required(VERSION 3.17)
project(CALC_TEST)
set(CMAKE_CXX_STANDARD 11)set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)add_executable(main ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)target_link_libraries(main pthread)

target_link_libraries(main pthread) 中:

  • main : 对应的是最终生成的可执行程序的名字
  • pthread :这是可执行程序要加载的动态库,这个库是系统提供的线程库,全名为libpthread.so ,在指定的时候一般会掐头(lib)去尾(.so)。

注意:

file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) 中递归包含了 src 下(包含子文件夹)所有的 .cpp 文件

链接第三方动态库

先前我们自己生成了一个动态库 libcalcso.so

此时的工程目录:

.
├── CMakeLists.txt
├── bin
│   └── main
├── include
│   └── head.h
├── lib
│   └── libcalcso.so
├── main.cpp
└── src├── add.cpp├── div.cpp├── mul.cpp└── sub.cpp

我们在测试的 main.cpp 中使用了自己制作的动态库 libcalcso.so ,如果还使用了系统提供的 libpthread.so ,则此时 CMakeLists.txt 应该这样写:

cmake_minimum_required(VERSION 3.17)
project(CALC_TEST)
set(CMAKE_CXX_STANDARD 11)set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)# 指定链接的第三方动态库的目录,如果只用系统的动态库,可以不写这个
link_directories(${PROJECT_SOURCE_DIR}/lib)add_executable(main ${SRC_LIST})target_link_libraries(main calcso pthread)

第 15 行的 calcso pthread 都是可执行程序 main 要链接的动态库的名字

在 build 目录下照常 cmake ..make,就能得到生成的可执行文件:

make
Consolidate compiler generated dependencies of target main
[ 50%] Linking CXX executable ../bin/main
[100%] Built target maintree ../bin
../bin
└── main0 directories, 1 file

运行该文件:

cd ../bin
./main
9.32 19.4052 -3.04 0.508091

如果运行 main 程序提示如下信息,说明加载动态库失败,原因为不知道这个动态库放到了什么位置:

./main: error while loading shared libraries: libcalcso.so: cannot open shared object file: No such file or directory

在 CMake 中可以在生成可执行程序之前,通过命令指定出要链接的动态库的位置,指定静态库位置使用的也是这个命令:

link_directories(path)

注意:这里的 path 是包含了动态库所在目录的路径,不是具体某一个动态库的绝对路径。

此外,Windows 系统的 MinGW 环境可用上面的CMakeLists.txt 文件,但是要成功运行该文件,可以将 .dll 文件和 .exe 文件放在同一个目录下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/743995.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Performance Monitoring检测camstar性能

InsiteXMLServer \ Provate Bytes使用内存的字节 InsiteXMLServer \ Working Set Peak 高峰 Process \ %Processor Time CPU占用时间 InsiteXMLServer \ Elapsed Time占用时间 Camstar.Security.LMServer \ Elapsed Time CamstarNotificatuionServer\Elapsed Time CIMSagent \ …

【Bug】拓展方法必须在非泛型静态类中定义

原文链接:https://blog.csdn.net/weixin_44231544/article/details/121752347 原: 修改: 拓展方法1.定义: (1)扩展方法能使你能够向现有类型添加“添加”方法,而无需创建新的派生类型,重新编译或以其他方式修改原始类型。 (2)扩展方法是一种特殊的静态方法,但可以像…

kettle从入门到精通 第七十五课 ETL之kettle血缘,数据血缘

在了解kettle血缘之前,咱们先来了解下什么是数据血缘? 1、数据血缘定义(来自gpt) 数据血缘(Data Lineage)是指在数据管理和数据分析中追踪数据的源头、流向和处理过程的能力。具体来说,数据血缘描述了数据如何被创建、变换和移动,以及这些过程中数据的路径和影响。它有…

kettle从入门到精通 第七五课 ETL之kettle血缘,数据血缘

在了解kettle血缘之前,咱们先来了解下什么是数据血缘? 1、数据血缘定义(来自gpt) 数据血缘(Data Lineage)是指在数据管理和数据分析中追踪数据的源头、流向和处理过程的能力。具体来说,数据血缘描述了数据如何被创建、变换和移动,以及这些过程中数据的路径和影响。它有…

如何实现元素的曝光监测

我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。本文作者:霁明一些名词解释 曝光 页面上某一个元素、组件或模块被用户浏览了,则称这个元素、组件或模块被曝光了。 视图元素 将页面上展示的…

MySQL - [17] Oracle、SQLServer、MySQL数据类型对比

题记部分 — 业精于勤荒于嬉,行成于思毁于随 —

华为云DTSE助力虎彩鲜檬优化运维效率,助力迁移上云

本文主要介绍了鲜檬摄影在数字化变革中,与华为云紧密合作,借助其DTSE专家团队,成功完成了业务系统改造、技术方案优化及迁移上云。本文分享自华为云社区《虎彩鲜檬数字化产业变革,迁移上云,助力其打造智慧婚拍新纪元》,作者: HuaweiCloudDeveloper。 来源:《华为云DTSE…

【CTF入门】BUUCTF Misc刷题(持续更新)

【CTF入门】BUUCTF Misc刷题 签到 点开发现签到题直接把flag交出来了,直接复制提交即可考点:了解CTF中flag的格式一般为flag{}

Linux 中 sed命令在指定行前添加空行

001、[root@PC1 test]# ls a.txt [root@PC1 test]# cat a.txt 33 aa 44 aa 88 aa 33 aa ff kk 33 aa 88 22 mm 88 33 44 22 99 [root@PC1 test]# sed 2 s/.*/\n&/ a.txt ## 在第二行前添加空行 33 aa 44 aa 88aa 33 aa ff kk 33 aa 88 22 mm 88 33 44 22 99 002、sed[roo…

nuxtjs2.x项目PC移动互相跳转

1、在plugins目录下新建terminalToggle.js,写入以下代码 (function () {let sUserAgent = navigator.userAgent.toLowerCase();let isIpad = sUserAgent.match(/ipad/i) == "ipad";let isIphoneOs = sUserAgent.match(/iphone os/i) == "iphone os";let i…

小车轮子测试(I2C)

I2C以及小车轮子测试I2C总线是PHILIPS公司推出的一种在电子通信控制领域常用的串行通信总线,是一种简单、双向 通信、二线制、同步的串行总线,具有连接线少、控制简单、通信速率高等优点。 端口检测 cd /dev lsI2C读写操作 使用i2cdetect命令检测i2c总线 i2cdetect -l如果红色…

Simple WPF: WPF使用Windows API发送Toast通知

以前看到Windows 10的气泡通知觉得很有意思,但是一直不知道该如何实现。最近一次上网冲浪过程中偶然的机会看到了相关资料就自己来试试。本文介绍了在WPF框架下发送Win10 Toast通知的方法。以前看到Windows 10的气泡通知觉得很有意思,但是一直不知道该如何实现。最近一次上网…