[C/C++开发]链接动态库在不同操作系统上的行为

news2024/10/5 9:05:35

个人博客:Sekyoro的博客小屋
个人网站:Proanimer的个人网站
想必很多人已经了解了动态库与静态库,在实际开发中也经常使用. 但是,有必要了解在windows和Linux上开发c++程序生成和链接动态库的不同行为,因为经常混淆或者自以为找到了动态库,这里简单学习并澄清一下.其中许多内容来自官方文档
在linux上静态库常常以.a结尾,动态库以.so结尾,而windows上分别以.lib与.dll结尾. 于是很多人就把.dll等同于.so使用了,但其实并不一样.

编译与链接静态库

生成动态库

gcc -shared -fPIC -o libfoo.so foo.c

生成静态库

gcc -o libfoo.o foo.c -I include 
ar cr libfoo.a libfoo.o
选项作用
-ggdb此选项将尽可能的生成 gdb 的可以使用的调试信息.
-l [lib](这里是小写的L,命令无中括号,下同)指定程序要链接的库,[lib]为库文件名称.如果gcc编译选项中加入了“-static”表示寻找静态库文件
-L [dir]指定-l(小写-L)所使用到的库文件所在路径(链接时而非动态查找),不然编译器将只在标准库的目录找
-I [dir](这里是大写的I)增加 include 头文件路径
-static链接静态库生成目标文件,禁止使用动态库(在支持动态链接的系统上) 所以编译出来的东西一般都很大,也不需要什么动态连接库就可以运行.
-shared生成共享文件,可以与其它文件链接生成可执行文件
-fpic生成适用于共享库的与地址无关的代码(PIC)(如果机器支持的话)
-fPIC生成与位置无关的的代码,适用于使用动态库,与“-fpic”的区别在于去除去全局偏移表的任何限制(如果机器支持的话)

链接动态库和链接静态库差别不大,但是需要设置一些路径方便linux查找.

gcc -o main main.c -I/home/alice/foo -lfoo

如果静态库和动态库在同一目录并且前缀相同,e.g. libxx.so和libxx.a,使用 g++ -o main main.cpp -L build/lib -l xx -I lib/include会默认链接动态库,添加-static可解决(不过一般也不会同时把动态库和静态库同名放一个目录吧😅)

gcc -L -l含义是什么,不管是动态库还是链接库,如果使用了库,都需要使用-L-l进行编译时链接,如果是静态库,-l往往就够了,但如果是动态库,-l作用是在编译时让编译器直到用到了库中的某些东西存在,但是运行时还需要另外设置,如果链接动态库不使用-l也会报错

在这里插入图片描述

g++ -o main main.cpp   ./build/lib/libdy_lib.so -I lib/include

或者直接使用.so.cpp编译链接,注意这在windows上行不通,根本原因是动态库的路径搜索方式不同

文件一多,项目一大肯定需要使用构建系统的,包括make,Ninja,MSBuilg等等,而cmake就是生成这些构建系统的,当使用cmake时就没有太大必要考虑编译器细节了.

# 生成
add_library(dy_lib STATIC test.cpp)
# add_library(dy_lib SHARED test.cpp)
# 链接
target_link_libraries(cpp_test PRIVATE dy_lib)

链接动态库在cmake中的行为

RPATH在开发过程中很有用,因为可以将构建树中的库链接到可执行文件中.CMake提供了相当多的选项来优化构建树链接和安装链接期间的行为

我们知道要使用动态库,光是-l是不行的,在windows上需要看链接方式(下面详细介绍),在linux上也要设置动态库搜索路径. 使用cmake时链接动态库,cmake会默认设置buil_rpath,但安装时使用install_rpathBUILD_RPATH — CMake 3.30.4 Documentation

BUILD_PATH

一个分号分隔的列表,指定要添加到构建树中链接的二进制文件中的运行时路径(RPATH)条目(对于支持它的平台).默认情况下,CMake在构建树中设置二进制文件的运行时路径,以包含它知道需要查找它们链接的共享库的搜索路径.项目可以设置BUILD_RPATH来指定额外的搜索路径.

  • The CMAKE_SKIP_RPATH variable completely disables runtime paths in both the build tree and install tree.
  • The SKIP_BUILD_RPATH target property disables setting any runtime path in the build tree.
  • The BUILD_RPATH_USE_ORIGIN target property causes the automatically-generated runtime path to use entries relative to $ORIGIN.
  • The BUILD_WITH_INSTALL_RPATH target property causes binaries in the build tree to be built with the install-tree runtime path.

下面是默认设置.默认情况下,如果不更改任何 RPATH 相关设置,CMake 将以完整的 RPATH 连接可执行文件和共享库,并将其连接到联编树中所有使用过的库.安装时,它会清除这些目标的 RPATH,因此它们在安装时的 RPATH 为空

# use, i.e. don't skip the full RPATH for the build tree
set(CMAKE_SKIP_BUILD_RPATH FALSE)

# when building, don't use the install RPATH already
# (but later on when installing)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)

# the RPATH to be used when installing
set(CMAKE_INSTALL_RPATH "")

# don't add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)

也就是说cmake在build时默认添加在build目录下使用的动态库路径,在安装库时rpath默认为空

Linux

在Linux系统中,动态链接器(如ld-linux.so)负责在应用程序启动时解析其依赖的共享库.动态链接器根据一定的搜索顺序来查找这些共享库,这个顺序通常包括:

  1. RPATH:如果可执行文件中指定了RPATH,动态链接器会首先在这个路径下搜索共享库.
  2. LD_LIBRARY_PATH:如果未找到所需的库,动态链接器会继续在由环境变量LD_LIBRARY_PATH指定的目录中搜索.
  3. 配置文件/etc/ld.so.conf
  4. 系统默认路径:如果仍未找到,动态链接器会在系统默认的库路径(如/lib/usr/lib)中搜索.

理解这个搜索机制有助于我们更好地掌握如何通过调整RPATH来控制应用程序的动态链接行为

Unless loading object has RUNPATH:
    RPATH of the loading object,
        then the RPATH of its loader (unless it has a RUNPATH), ...,
        until the end of the chain, which is either the executable
        or an object loaded by dlopen
    Unless executable has RUNPATH:
        RPATH of the executable
LD_LIBRARY_PATH
RUNPATH of the loading object
ld.so.cache
default dirs

RPATH

rpath优先级最高,会优先让执行档去寻找相应的动态库(如果设置了RUNPATH就会忽略RPATHc - use RPATH but not RUNPATH? - Stack Overflow,简单来说,如果设置了RUN_PATH,那么RPATH会被忽略,但是RUNPATH优先级又低于LD_LIBRAY_PATH

作者给的建议是当您发布二进制文件时,要么使用RPATH而不是RUNPATH,要么确保在运行它们之前设置了LD_LIBRARY_PATH,当然也有推荐只使用LIBRARY_PATH的.

注意,runpath和rpath也许操作系统支持有关,新版本的os应该默认使用runpath了,也就是使用gcc -rpath时默认设置runpath

设置rpath,告诉新系统使用老行为

gcc ... -Wl --disable-new-dtags -rpath=""

设置runpath,告诉旧系统使用新行为

gcc ... -Wl --enable-new-dtags -rpath=""

查看一个elf文件的PATH,可以看到目前默认是runpath,rpath是depreacated了,这两者最大差异就是优先级

readelf --dynamic obj | grep PATH

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fs2.loli.net%2F2024%2F10%2F04%2Fw

使用cmake开发时,默认就是这样使用动态库的

LIBRAY_PATH

LIBRAY_PATH不是运行时搜索动态库,其效果类似于gcc -L,设置编译时查找路径

export LIBRARY_PATH=/home/foo
gcc -o main main.c -I/home/foo -lfoo
ls
main  main.c

推荐使用gcc -L即可

事实上实践中直接使用cmake

link_directories()
target_link_libraries()

LD_LIBRAY_PATH

你会发现在链接动态库后执行程序也无法成功,因为linux搜索动态库的路径并没有包括动态库的路径,道理同rpath

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/work
./main

/etc/ld.so.conf

将非标准路经加入 /etc/ld.so.conf,然后运行 ldconfig 生成 /etc/ld.so.cache. ld.so 加载共享库的时候,会从 ld.so.cache 查找

vim /etc/ld.so.conf
# 在文件中添加库路径 e.g. /project/build/libdy_lib.so
sudo ldconfig

原理是ldconfig这个程序,程序运行时会通过这个程序查找库.

默认搜索路径

可执行程序动态库默认搜索路径包括/usr/lib和/lib

cp libdy_lib.so /lib
# 以下命令均可
cp libdy_lib.so /usr/lib
ln -s libdy_lib.so /usr/lib

Windows

windows并没有类似linux的rpath机制dll - Is there a Windows/MSVC equivalent to the -rpath linker flag? - Stack Overflow

当你使用visual studio开发使用了动态库时,也许你在vs上执行并没有问题,但直接点击可执行程序执行就报错了(即使你给可执行程序添加了相关引用). 主要原因是vs在编译链接时会去引用生成的目录找相关.dll和.lib(即使是动态库,vs也会生成DLL导入库,这类似一个查找表,方便获取DLL中的函数、变量等)

而在运行时,动态库的查找机制就不一样了

在这里插入图片描述

链接方法

可执行文件通过以下两种方式之一链接到(或加载)DLL:

  • 隐式链接,其中操作系统会与使用 DLL 的可执行文件同时加载它. 客户端可执行文件调用 DLL 的导出函数的方式与函数进行静态链接并包含在可执行文件中时的方式相同. 隐式链接有时称为静态加载或加载时动态链接.
  • 显式链接,其中操作系统会在运行时按需加载 DLL. 通过显式链接使用 DLL 的可执行文件必须显式加载和卸载 DLL. 它还必须设置函数指针,用于访问它从 DLL 使用的每个函数. 与静态链接的库或隐式链接 DLL 中的函数调用不同,客户端可执行文件必须通过函数指针调用显式链接 DLL 中的导出函数. 显式链接有时称为动态加载或运行时动态链接.
隐式链接

当应用程序的代码调用导出 DLL 函数时,会进行隐式链接. 当编译或汇编调用可执行文件的源代码时,DLL 函数调用会在对象代码中生成外部函数引用.

若要解析此外部引用,应用程序必须与 DLL 创建者提供的导入库(.lib 文件)链接.

导入库包含的代码仅用于加载 DLL 和实现对 DLL 中函数的调用. 在导入库中查找外部函数会告知链接器该函数的代码处于 DLL 中. 若要解析对 DLL 的外部引用,链接器只需将信息添加到可执行文件,告知系统在进程启动时查找 DLL 代码的位置.

当系统启动包含动态链接引用的程序时,它将使用该程序可执行文件中的信息查找所需 DLL. 如果找不到 DLL,则系统将终止进程,并显示报告错误的对话框. 否则,系统会将 DLL 模块映射到进程地址空间中.

所以我们需要一个.lib文件方便静态加载,也就是程序在编译时就知道了动态库的位置(通过.lib),这样方便查找,而不是像上面提到的linux再通过rpath等路径去看. 那这样做需要什么呢? 那就是经典的__declspec(dllexport)了dllexport、dllimport | Microsoft Learn

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )

DllExport void func();
DllExport int i = 10;
DllImport int j;
DllExport int n;

使用 dllexport 意味着定义,而使用 dllimport 则意味着声明. 必须使用带 externdllexport 关键字来强制进行声明;否则,会进行隐式定义.

static __declspec( dllimport ) int l; // Error; not declared extern.

void func() {
    static __declspec( dllimport ) int s;  // Error; not declared
                                           // extern.
    __declspec( dllimport ) int m;         // Okay; this is a
                                           // declaration.
    __declspec( dllexport ) int n;         // Error; implies external
                                           // definition in local scope.
    extern __declspec( dllimport ) int i;  // Okay; this is a
                                           // declaration.
    extern __declspec( dllexport ) int k;  // Okay; extern implies
                                           // declaration.
    __declspec( dllexport ) int x = 5;     // Error; implies external
                                           // definition in local scope.
}

当声明 dllexport 类时,它的所有成员函数和静态数据成员都会导出. 必须在同一程序中提供所有此类成员的定义. 否则,将生成链接器错误. 此规则有一个例外情况,即对于纯虚函数,无需为其提供显式定义. 但是,由于基类的析构函数始终在调用继承类的析构函数,因此纯虚析构函数必须始终提供定义

当声明 dllimport 类时,它的所有成员函数和静态数据成员都会导入. 与非类类型上的 dllimportdllexport 的行为不同,静态数据成员无法在定义 dllimport 类的同一程序中指定定义. 如果整个类都已导入或导出,则禁止将成员函数和数据显式声明为 dllimportdllexport

#define DllExport   __declspec( dllexport )

class DllExport C {
   int i;
   virtual int func( void ) { return 1; }
};
// lib_link_input_2.cpp
// compile by using: cl /EHsc lib_link_input_1.lib lib_link_input_2.cpp
__declspec(dllimport) int Test();
#include <iostream>
int main() {
   std::cout << Test() << std::endl;
}

在windows上,这几乎成了常用的方式,不管你使用的什么编译器,即便是mingw,clang

如果使用vc++,那会生成xx.dll与libxx.lib,后者用于找动态库,而使用mingw,clang会生成xx.dll和xx.dll.a,效果一样.

若要通过隐式链接使用 DLL,客户端可执行文件必须从 DLL 的提供程序获取以下文件:

  • 一个或多个头文件(.h 文件),其中包含 DLL 中的导出数据、函数和 C++ 类的声明. DLL 导出的类、函数和数据全都必须在头文件中标记为 __declspec(dllimport)
  • 要链接到可执行文件中的导入库. 生成 DLL 时,链接器会创建导入库
  • 实际 DLL 文件.

我们在windows上默认都是使用的隐式链接,如果你要使用动态库,还挺麻烦的.

显式链接

有时需要显式链接. 下面是使用显式链接的一些常见原因:

  • 应用程序直到运行时才知道它所加载的 DLL 的名称. 例如,应用程序可能会在启动时从配置文件获取 DLL 的名称和导出函数.
  • 如果在使用隐式链接的进程启动时找不到 DLL,则操作系统会终止进程. 使用显式链接的进程在这种情况下不会终止,可以尝试从错误中恢复. 例如,进程可以向用户通知错误,并让用户指定 DLL 的其他路径.
  • 如果使用隐式链接的进程所链接到的任何 DLL 的 DllMain 函数失败,则进程也会终止. 使用显式链接的进程在这种情况下不会终止.
  • 隐式链接到许多 DLL 的应用程序可能会速度较慢,因为 Windows 会在应用程序加载时加载所有 DLL. 若要提高启动性能,应用程序可以只对在加载之后立即需要的 DLL 使用隐式链接. 它可以仅在需要时才使用显式链接加载其他 DLL.
  • 显式链接无需使用导入库链接应用程序. 如果 DLL 中的更改导致导出序号发生更改,则在使用函数名称而不是序号值调用 GetProcAddress 时,应用程序无需重新链接. 使用隐式链接的应用程序仍必须重新链接到更改的导入库.

若要通过显式链接使用 DLL,应用程序必须在运行时进行函数调用以显式加载 DLL. 若要显式链接到 DLL,应用程序必须:

  • 调用LoadLibraryEx或类似函数以加载 DLL 并获取模块句柄.
  • 调用 GetProcAddress以获取应用程序调用的每个导出函数的函数指针. 由于应用程序通过指针调用 DLL 函数,因此编译器不生成外部引用,从而不需要与导入库链接. 不过必须有 typedefusing 语句,此语句定义调用的已导出函数的调用签名.
  • 处理完 DLL 时,调用 FreeLibrary
#include "windows.h"

typedef HRESULT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT*);

HRESULT LoadAndCallSomeFunction(DWORD dwParam1, UINT * puParam2)
{
    HINSTANCE hDLL;               // Handle to DLL
    LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer
    HRESULT hrReturnVal;

    hDLL = LoadLibrary("MyDLL");
    if (NULL != hDLL)
    {
        lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");
        if (NULL != lpfnDllFunc1)
        {
            // call the function
            hrReturnVal = lpfnDllFunc1(dwParam1, puParam2);
        }
        else
        {
            // report the error
            hrReturnVal = ERROR_DELAY_LOAD_FAILED;
        }
        FreeLibrary(hDLL);
    }
    else
    {
        hrReturnVal = ERROR_DELAY_LOAD_FAILED;
    }
    return hrReturnVal;
}

动态库查找路径

查找路径不仅针对显式链接,隐式链接也能用. 比较方便的就是可执行程序文件、或环境变量PATH

当应用程序调用 LoadLibrary或 LoadLibraryEx函数时,系统会尝试查找 DLL . 如果搜索成功,系统会将 DLL 模块映射到进程的虚拟地址空间,并递增引用计数.

Dynamic-link library search order - Win32 apps | Microsoft Learn

windows搜索dll路径顺序比较麻烦,需要看是否是打包应用(loadPackagedLibrary ),是否开启了安全DLL搜索模式(默认开启)

若要禁用安全 DLL 搜索模式,将HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode 创建注册表值并将其设置为 0

如果是未打包并且是安全搜索模式,那么搜索顺序如下,前六个感觉不用看

  1. DLL 重定向.
  2. API sets.
  3. SxS manifest redirection.
  4. Loaded-module list.
  5. Known DLLs.
  6. Windows 11,版本 21H2 (10.0;内部版本 22000) 及更高版本. The package dependency graph of the process. This is the application’s package plus any dependencies specified as <PackageDependency> in the <Dependencies> section of the application’s package manifest. Dependencies are searched in the order they appear in the manifest.
  7. 从中加载应用程序的文件夹.
  8. 系统文件夹. 使用GetSystemDirectory函数检索此文件夹的路径.
  9. 16 位系统文件夹. 没有获取此文件夹路径的函数,但会对其进行搜索.
  10. Windows 文件夹. 使用GetWindowsDirectory函数获取此文件夹的路径.
  11. 当前文件夹.
  12. 环境变量中列出的 PATH 目录. 这不包括由应用路径注册表项指定的App Paths . 计算 DLL 搜索路径时,不使用 App Paths 变量

如果禁用安全DLL 搜索模式,则搜索顺序基本相同,只是位置11和8交换顺序

总结

总结一下,在linux使用cmake开发c/c++程序链接动态库时使用rpath添加搜索目录,使用windows开发开发动态库实在麻烦,一般默认隐式链接然后使用 __declspec( dllexport)导出(因为默认不导出),如果使用现成的xx.dll和libxx.lib就不需要声明__declspec(__dllimport)宏了,因为链接了DLL导入库(也就是libxx.lib) Using Dynamic Libraries in C++ (youtube.com)

至于生成的DLL放哪,连微软自己都说放在可执行文件同一目录中,在vs中可将“后期生成事件”添加到项目中,以此添加一条命令,将 DLL 复制到生成输出目录.

xcopy /y /d "..\..\MathLibrary\$(IntDir)MathLibrary.dll" "$(OutDir)"

我的配置如下
在这里插入图片描述

xcopy /y /d "$(OutDir)$(TargetFileName)" "$(SolutionDir)bin\$(Platform)\$(Configuration)\"

如果使用cmake,添加自定义command将DLL放在可执行程序同一目录,道理相同,使用了cmake -E

add_custom_command(TARGET MyTest POST_BUILD        
    COMMAND ${CMAKE_COMMAND} -E copy_if_different  
        "${PROJECT_SOURCE_DIR}/libs/test.dll"      
        $<TARGET_FILE_DIR:MyTest>)                 

或者类似的使用更好的生成器表达式$<TARGET_RUNTIME_DLLS:MyTest>

find_package(foo CONFIG REQUIRED) # package generated by install(EXPORT)

add_executable(exe main.c)
target_link_libraries(exe PRIVATE foo::foo foo::bar)
add_custom_command(TARGET exe POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_FILE_DIR:exe> $<TARGET_RUNTIME_DLLS:exe>
  COMMAND_EXPAND_LISTS
)

cmake -E copy_if_different

copy <file>... destination  - copy files to destination (either file or directory)
copy_directory <dir>... destination   - copy content of <dir>... directories to 'destination' directory
copy_directory_if_different <dir>... destination   - copy changed content of <dir>... directories to 'destination' directory
copy_if_different <file>... destination  - copy files if it has changed

参考资料

  1. 将可执行文件链接到 DLL | Microsoft Learn
  2. 演练:创建和使用自己的动态链接库 (C++) | Microsoft Learn
  3. gcc/g++ 动态库和静态库,编译与链接(含示例)_g++ 链接静态库-CSDN博客
  4. LIBRARY_PATH vs LD_LIBRARY_PATH | Baeldung on Linux
  5. 【Linux 应用开发 】Linux环境下动态链接库路径(RPATH)的调整策略-阿里云开发者社区 (aliyun.com)
  6. Linux动态库(.so)搜索路径 - AndyJee - 博客园 (cnblogs.com)
  7. RPATH and RUNPATH (archive.org)
  8. RPATH, RUNPATH, and dynamic linking (tremily.us)
  9. RPATH handling · Wiki · CMake / Community · GitLab (kitware.com)
  10. How to copy DLL files into the same folder as the executable using CMake? - Stack Overflow
  11. cmake-generator-expressions(7) — CMake 3.30.4 Documentation

如有疑问,欢迎各位交流!

服务器配置
宝塔:宝塔服务器面板,一键全能部署及管理
云服务器:阿里云服务器
Vultr服务器: Vultr服务器
GPU服务器:Vast.ai
代码练习平台
CodeCrafters CodeCrafters

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python】pyenv:管理多版本 Python 环境的利器

pyenv 是一个强大的 Python 版本管理工具&#xff0c;它允许开发者在同一台计算机上轻松安装和管理多个 Python 版本。对于需要在不同项目中使用不同 Python 版本的开发者来说&#xff0c;pyenv 是一个非常有用的工具&#xff0c;因为它可以帮助用户在全局和项目级别控制 Pytho…

PCL 给点云添加高斯噪声并保存

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 添加高斯噪声实现 2.1.2 可视化函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&…

角色动画——RootMotion全解

1. Unity(2022)的应用 由Animtor组件控制 在Animation Clip下可进行详细设置 ​ 官方文档的介绍(Animation选项卡 - Unity 手册) 上述动画类型在Rag选项卡中设置: Rig 选项卡上的设置定义了 Unity 如何将变形体映射到导入模型中的网格&#xff0c;以便能够将其动画化。 对于人…

污水排放口细粒度检测数据集,污-水排放口的类型包括10类目标,10000余张图像,yolo格式目标检测,9GB数据量。

污水排放口细粒度检测数据集&#xff0c;污-水排放口的类型包括10类目标&#xff08;1 合流下水道&#xff0c;2 雨水&#xff0c;3 工业废水&#xff0c;4 农业排水&#xff0c;5 牲畜养殖&#xff0c;6 水产养殖&#xff0c;7 地表径流&#xff0c;8 废水处理厂&…

yub‘s Algorithmic Adventures_Day5

Day5 反转链表 link&#xff1a;206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 思路分析 与数组不同&#xff0c;链表没必要定义新的链表进行存储【对内存空间的浪费】 直接改变next指针即可. 注意头节点指向的下一个节点为null 双指针法 class Solution {publi…

杂谈c语言——6.浮点数的存储

1.浮点数在内存中的存储 常⻅的浮点数&#xff1a;3.14159、1E10等&#xff0c;浮点数家族包括&#xff1a; float、double、long double 类型。 浮点数表⽰的范围&#xff1a; float.h 中定义 1.1 练习 #include<stdio.h>int main() {int n 9;float* pFloat (floa…

Golang | Leetcode Golang题解之第456题132模式

题目&#xff1a; 题解&#xff1a; func find132pattern(nums []int) bool {candidateI, candidateJ : []int{-nums[0]}, []int{-nums[0]}for _, v : range nums[1:] {idxI : sort.SearchInts(candidateI, 1-v)idxJ : sort.SearchInts(candidateJ, -v)if idxI < idxJ {ret…

智能视界·大模型驱动视频矩阵管理系统

开头先配两张ER图 一张不带字段&#xff0c;一张带字段&#xff0c;剩下的内容按需拿取 1.产品介绍 产品名称&#xff1a; 智能视界大模型驱动视频矩阵管理系统 主要功能&#xff1a; 智能视频分析与识别 功能介绍&#xff1a;该系统集成先进的人工智能大模型&#xff0c;能…

热轧钢带缺陷数据集,Xsteel表面缺陷数据集(X-SDD),其中包含七种典型的热轧带钢缺陷类型,共有1360个缺陷图像。

热轧钢带缺陷数据集&#xff0c;称为Xsteel表面缺陷数据集&#xff08;X-SDD&#xff09;&#xff0c;其中包含七种典型的热轧带钢缺陷类型&#xff0c;共有1360个缺陷图像。与常用的NEU表面缺陷数据库&#xff08;NEU-CLS&#xff09;的六种缺陷类型相比&#xff0c;X-SDD包含…

Ray_Tracing_The_Next_Week

1动态模糊 动态模糊在摄影中就是快门的速度慢&#xff0c;捕捉光的时间长&#xff0c;物体运动时进行捕捉成像&#xff0c;拍出来的结果是这个运动过程每一帧的平均值 我们的思路是&#xff1a; 每一条光线都拥有自己存在的一个时间点。随着时间变化随机生成光线,一般来说我…

全新芒果YOLOv10改进135:最新注意力机制EMA:即插即用,具有跨空间学习的高效多尺度注意力模块,ICCASSP 2023

💡本篇内容:芒果YOLOv10改进135:最新注意力机制EMA:即插即用,具有跨空间学习的高效多尺度注意力模块,ICCASSP 2023 **具有跨空间学习的高效多尺度注意力模块EMA | 即插即用 该模块通常包括多个并行的注意力子模块,每个子模块关注于输入数据的不同尺度或分辨率。这些子模块…

Study-Oracle-10-ORALCE19C-RAC集群维护

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。 一、RAC的逻辑架构与进程 1、RAC 与单实例进程的对比 2、RAC相关进程功能 3、在主机查看RAC进程 其他的不列举了 4、RAC集群启停命令 检查集群状态 ORACLE 19C …

2-112基于matlab的协同干扰功率分配模型

基于matlab的协同干扰功率分配模型&#xff0c;带操作界面的功率分配GUI&#xff0c;可以实现对已有功率的分配优化&#xff0c;可以手动输入参数值。4个干扰山区分二批总干扰功率&#xff0c;每个扇区包括威胁总系数、综合压制概率、目标函数增量等。程序已调通&#xff0c;可…

无源有损耗导电介质的平面电磁波——复数介电常数带来复波数k(导致幅度衰减)和复波阻抗(带来磁场电场相位不同)

推导中以εμσ是实数为假设 注意在线性介质中J 0和σ等于0其实是一个条件&#xff0c;因为J σE 线性介质的麦克斯韦方程 线性介质无源无损耗条件下 线性介质无源有损耗导电介质下 无源有损耗的复数麦克斯韦方程组&#xff0c;只有方程二与无源无损耗的麦克斯韦方程组不同…

【Linux】使Ubuntu自适应窗口大小并与主机共享文件

LInux虚拟机版本ubuntu-20.04.6&#xff0c;VM版本VMware Workstation 17 Pro VMware Tools™ 是一组服务和模块&#xff0c;是VMware公司在其虚拟化平台中提供的一套工具集&#xff0c;旨在提高虚拟机的性能和稳定性。它们支持 VMware 产品中的多种功能特性&#xff0c;有助于…

TX-LCN框架 分布式事务

一、三种事务模式 1&#xff09;LCN 基于XA协议&#xff0c;事务提交或回滚的操作由事务管理服务器统一告诉它管理的多个项目&#xff0c;也就是说在A事务&#xff0c;B事务的事务提交操作或回滚操作都是在同一时刻发生&#xff0c;并且要么都提交&#xff0c;要么都回滚。 LCN…

.NET开源跨平台桌面和移动应用的统一框架 - Eto.Forms

前言 今天大姚给大家分享一个.NET开源、跨平台桌面和移动应用的统一框架&#xff1a;Eto.Forms。 项目介绍 Eto.Forms是一个.NET开源、跨平台的桌面和移动应用的统一框架&#xff0c;该框架允许开发者使用单一的UI代码库构建在多个平台上运行的应用程序&#xff0c;并利用各…

如何修改银河麒麟高级服务器操作系统修改网卡名称(如从ens33到eth0)

如何修改银河麒麟高级服务器操作系统修改网卡名称&#xff08;如从ens33到eth0&#xff09; &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 查找并修改配置文件&#xff1a; 使用ifconfig查看网卡名称&#xff08;如enp1s0&#xff09;。找…

小白快速上手 Docker 03 | Docker数据卷

数据卷 在前面使用Docker时&#xff0c;可能会遇到以下几个问题&#xff1a; 当Docker 里的容器挂了以后打不开&#xff0c;这时候只有删除该容器了&#xff0c;但删除容器会连容器中的产生的数据也一起删除了&#xff0c;大部分场景下这是不能接受的。Docker容器与容器之间不…

【D3.js in Action 3 精译_028】3.4 小节 DIY 实战:使用 Observable 在线绘制 D3 条形图

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…