[笔记]Windows核心编程《十九》DLL基础

这篇具有很好参考价值的文章主要介绍了[笔记]Windows核心编程《十九》DLL基础。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章目录

[笔记]Windows核心编程《一》错误处理、字符编码
[笔记]Windows核心编程《二》内核对象
[笔记]Windows核心编程《三》进程
[笔记]Windows核心编程《四》作业
[笔记]快乐的LInux命令行《五》什么是shell
[笔记]Windows核心编程《五》线程基础
[笔记]Windows核心编程《六》线程调度、优先级和关联性
[笔记]Windows核心编程《七》用户模式下的线程同步
[笔记]Windows核心编程《八》用内核对象进行线程同步
[笔记]Windows核心编程《九》同步设备I/O和异步设备I/O
[笔记]Windows核心编程《十一》Windows线程池
[笔记]Windows核心编程《十二》纤程
[笔记]Windows核心编程《十三》windows内存体系结构
[笔记]Windows核心编程《十四》探索虚拟内存
[笔记]Windows核心编程《十五》在应用程序中使用虚拟内存
[笔记]Windows核心编程《十六》线程栈
[笔记]Windows核心编程《十七》内存映射文件
[笔记]Windows核心编程《十八》堆栈
[笔记]Windows核心编程《十九》DLL基础
[笔记]Windows核心编程《二十》DLL的高级操作技术
[笔记]Windows核心编程《二十一》线程本地存储器TLS
[笔记]Windows核心编程《二十二》注入DLL和拦截API
[笔记]Windows核心编程《二十三》结构化异常处理

前言

常用DLL

Windows API中的所有函数都包含在 D L L中。3个最重要的 D L L是:

  • Kernel32.dll:它包含用于管理内存、进程和线程的各个函数;
  • User32.dll:它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;
  • GDI32.dll,它包含用于画图和显示文本的各个函数。

其他:

  • AdvAPI32. dll 包含用于实现对象安全性、注册表操作和事件记录的函数;
  • ComDlg32.dll 包含常用对话框(如File Open和File Save);
  • ComCtl32.dll 则支持所有的常用窗口控件。

使用DLL的一些原因

优点:

  • 它们扩展了应用程序的特性。 由于 DLL能够动态地装入进程的地址空间,因此应用程序能够在运行时确定需要执行什么操作,然后装入相应的代码,以便根据需要执行这些操作。
  • 它们可以用许多种编程语言来编写。 可以选择手头拥有的最好的语言来编写 D L L。系统允许Visual Basic程序加载C++ DLL、Cobol DLL和Fortran DLL等。
  • 它们简化了软件项目的管理。 项目小组分工可以使用DLL,项目管理会容易一些。但是过多的DLL会使程序加载时间过长。
  • 它们有助于节省内存。 如果两个或多个应用程序使用同一个 D L L,那么该D L L的页面只要放入R A M一次,所有的应用程序都可以共享它的各个页面。 C/C++运行期库就是个极好的例子。许多应用程序都使用这个库。如果所有的应用程序都链接到这个静态库,那么sprintf、strcpy和malloc等函数的代码就要多次存在于内存中。但是,如果所有这些应用程序链接到DLL C/C++运行期库,那么这些函数的代码就只需要放入内存一次,这意味着内存的使用将更加有效。
  • 它们有助于资源的共享。 DLL可以包含对话框模板、字符串、图标和位图等资源。多个应用程序能够使用DLL来共享这些资源。
  • 它们有助于应用程序的本地化。 应用程序常常使用 D L L对自己进行本地化。例如,只包含代码而不包含用户界面组件的应用程序可以加载包含本地化用户界面组件的 D L L。
  • 它们有助于解决平台差异。 它们有助于解决平台差异。不同版本的 Wi d n o w s配有不同的函数。开发人员常常想要调用新的函数(如果它们存在于主机的 Wi n d o w s版本上的话)。但是,如果你的源代码包含了对一个新函数的调用,而你的应用程序将要在不能提供该函数的 Wi n d o w s版本上运行,那么操作系统的加载程序将拒绝运行你的进程。即使你实际上从不调用该函数,情况也是这样。如果将这些新函数保存在 D L L中,那么应用程序就能够将它们加载到 Windows的老版本上。当然,你仍然可以成功地调用该函数。
  • 它们可以用于一些特殊的目的。 Wi n d o w s使得某些特性只能为 D L L所用。例如,只有当D L L中包含某个挂钩通知函数的时候,才能安装某些挂钩(使用 SetWindowsHookEx和SetWinEventHook来进行安装)。可以通过创建必须在DLL中生存的COM对象来扩展Windows Explorer的外壳程序。对于可以由We b浏览器加载的、用于创建内容丰富的 Web页的ActiveX控件来说,情况也是一样.

缺点:

  • 过多的DLL会使程序加载时间过长。

一、DLL与进程的地址空间

DLL特点:

  • DLL仅包含一组应用程序可以使用的自主函数。在DLL中通常没有用来处理消息循环或创建窗口的支持代码。

应用程序(或另一个D L L)能够调用D L L中的函数条件:

  • DLL文件 映像必须被映射到调用进程的地址空间中。

D L L映射到调用进程的地址空间的方式:

  • 加载时的隐含链接
  • 运行期的显式链接

DLL文件映射完后,就会像在进程的地址空间中的额外代码和数据一样:

  1. DLL函数查看线程堆栈,检索所传递的参数。
  2. 使用线程的堆栈初始化DLL函数需要的局部变量

注意:

  • exe加载dll时也会加载dll的全局变量和静态变量的实例 到静态区

关于跨DLL释放 解释

单个地址空间是由一个可执行模块和若干个 D L L模块组成的。

一般三种情况的模块:

  • 链接到静态版本的 C/C++运行期库的模块
  • 链接到一个DLL版本的 C/C++ 运行期库的模块
  • 不需要C/C++运行期库的模块

静态运行时库(MT): 编译时会包含一些C/C++运行时库,但是使用多个模块的大型软件来说,如果每个模块均选择静态链接C或C++运行库,程序运行时就会存在多个运行库。在链接时也会出现重复定义的问题。
[笔记]Windows核心编程《十九》DLL基础
动态运行时库 (MD): 程序在运行时动态的加载对应的DLL。程序体积变小,但一个很大的问题就是一旦找不到对应DLL,程序将无法运行(比如所要移植的电脑没有安装VC++)。
假设使用VC6.0并选择使用MD选项构建,那么当用户使用VC2005来使用这个DLL时很可能出现找不到MSVCRT.DLL或MSVCP60.DLL的情况。
终于理解了什么是c/c++运行时库,以及libcmt msvcrt等内容

请看下面的代码:

vOID EXEFunc( ) 
{
	PvOID pv = DLLFunc( );
	//Access the storage pointed to by pv. ..
	//Assumes that pv is in EXE's C/C++ run-time heap
	free(pv) ;
}

PVOID DLLFunc( ) 
{
	//A11ocate block from DLL's C/C++ run-time heap
	return(ma11oc(100 ));
}
  • 上面这个代码能够正确运行吗?
  • D L L函数分配的内存块是由EXE的函数释放的吗?

两种可能:

  • 如果EXE和DLL都链接到DLL的C/C++运行期库(MD),那么上面的代码将能够很好地运行。
  • 但是,如果两个模块中的一个或者两个都链接到静态C/C + +运行期库(MT),那么对free函数的调用就会失败。

解决方案就是:
当一个模块提供一个用于分配内存块的函数时,该模块也必须提供释放内存的函数。(谁申请内存 谁就去释放内存)、

VOID EXEFunc( ) 
{
	PVOID pv = DLLFunc( ) ;
	//Access the storage pointed to by pv.. .
	//Makes no assumptions about C/C++ run-time heap
	DLLFreeFunc(pv);
}

PVOID DLLFunc( )
{
	//A11ocate b1ock from DLL's C/C++ run-time heap
	PVOID pv = ma11oc(100) ;
	return(pv);
}

B00L DLLFreeFunc( PVOID pv) 
{
 //Free block from DLL's C/C++ run-time heap
 return( free(pv ) );
)

这个代码是正确的,它始终都能正确地运行。
当你编写一个模块时,不要忘记其他模块中的函数也许没有使用C/C + +来编写,因此可能无法使用malloc和free函数进行内存的分配。

二、 DLL的总体运行情况

本节重点介绍可执行模块和 DLL模块之间是如何隐含地互相链接。

当一个模块(比如一个可执行文件)使用DLL中的函数或变量时,将有若干个文件和组件参与发挥作用。如下图:
[笔记]Windows核心编程《十九》DLL基础

创造DLL:
1)建立带有输出原型/结构/符号的头文件。
2)建立实现输出函数/变量的C/C++源文件。
3)编译器为每个C/C++源文件生成 .obj模块 。
4)链接程序将生成DLL的 .obj模块链接起来
5)如果至少输出一个函数/变量,那么链接程序也生成lib 文件。

创造EXE:
6) 建立带有输入原型/结构/符号的头文件。
7) 建立引用输入函数/变量的C/C++源文件。
8) 编译器为每个C/C++源文件生成 .obj源文件。
9) 链接程序将各个 .obj模块链接起来,产生一个 .exe文件(它包含了所需要DLL模块的名字和输入符号的列表)。

运行应用程序:
10) 加载程序为 .exe 创建地址空间。
11) 加载程序将需要的DLL加载到地址空间中进程的主线程开始执行;应用程序启动运行。

几个文件解释
.h文件:一般是函数的声明,或者记录要导出函数的文件。
.cpp文件:一般就是函数的实现,一般不导出,只用于生成.obj文件。
.obj文件:cpp文件的编译后的产物。
.lib:列出所有已输出函数和变量的符号名。
.dll:记录函数的实现。
.exe :

  • 执行文件的所有二进制代码和全局/静态变量。
  • 输入节,列出可执行文件需要的所有DLL模块名以及所引用的函数和变量符号。

简单的来说就是编译链接运行的过程:

  1. 编译:把cpp编译成.obj。
  2. 链接:就是链接所有.obj到一个文件(dll/exe)中。
  3. 运行:为exe创建地址空间,加载dll到地址空间执行。

创建DLL模块步骤

若要创建D L L模块,必须执行下列操作步骤:

  1. 必须创建一个头文件,它包含你想要从D L L输出的函数原型、结构和符号。当使用DLL时,也是需要头文件的。

  2. 要创建一个.cpp文件,主要编写函数实现。对外隐藏。

  3. 编译.cpp为.obj。

  4. 链接所有.obj,产生一个DLL映象文件。该映像文件(即模块)包含了用于 DLL的所有二进制代码和全局 /静态数据变量。

  5. 生成一个.lib文件,lib文件包含所有已输出函数和变量的符号名。

创建可执行模块步骤

  1. 包含DLL模块的头文件。

  2. 创建Cpp文件,并引用dll模块所包含的函数和变量。

  3. 编译,生成.obj模块。

  4. 链接程序便将所有的. o b j模块的内容组合起来,生成一个可执行的映像文件。一旦DLL和可执行模块创建完成,一个进程就可以执行。

运行可执行模块时,操作系统的加载程序执行步骤

  1. 加载程序为新进程创建一个虚拟地址空间:
    1. exe被映射到新进程的地址空间。
    2. 加载程序对可执行模块的输入节进行分析。对于该节中列出的每个DLL名字,加载程序递归找出用户系统上的DLL模块,再将DLL以及DLL所需的映射到进程的地址空间。

注意,由于DLL模块可以从另一个DLL模块输入函数和变量,因此DLL模块可以拥有它自己的输入节(即记录了所需的DLL的函数名和变量符号)。若要对进程进行全面的初始化,加载程序要分析每个模块的输入节,并将所有需要的DLL模块映射到进程的地址空间。
如你所见,对进程进行初始化,DLL越多越费时间。

三、 创建D L L模块

D L L可以输出以下几种类型到其他模块。:

  • 变量
  • 函数
  • C / C + +类

注意:
1.应当避免输出变量。因为这会删除你的代码中的一个抽象层,使它更加难以维护你的 D L L代码。
2.应避免输出C + +类。只有当DLL模块开发人员和EXE开发人员使用的编译工具相同时,才可输出类。

下面的代码说明了应该如何对单个头文件进行编码,以便同时包含可执行文件和 D L L的源代码文件:

/****************************************
 Module:MyLib.h
****************************************/
#ifdef MYLIBAPI
//MYLIBAPI should be defined in all of the DLL's sourcel / code modules before this header file is included.1l A11 functions/variab1es are being exported.
#e1se
//This header file is included by an EXE source code module.ll Indicate that a11 functions/variables are being imported.
#define MYLIBAPI extern "C" __declspec(d11import)


// Define any data structures and symbo1s here.

// Define exported variables here. ( NOTE: Avoid exporting variables.)
MYLIBAPI int g_nResult;

//Define exported function prototypes here.
MYLIBAPI int Add( int nLeft. int nRight) ;
/****************************************
 Module:MyLib.cpp
****************************************/
//Include the standard windows and C-Runtime header files here.
#include <windows.h>
// This DLL source code file exports functions and variab1es.
#define MYLIBAPI extern "c" __dec1spec(d11export)
//Include the exported data structures,symbo1s,functions,and variables.
#include "MyLib.h"
//Place the code for this DLL source code file here.int g...nResult;
int Add( int nLeft, int nRight) {
	g_nResu1t = nLeft + nRight;return( g nResu1t);
}

在MyLib.h头文件的前面使用__declspec(dllexport)作用是:

  • 当编译器看到负责修改变量、函数或 C + +类的__declspec(dllexport)时,它就知道该变量、函数或C + +类是从产生的DLL模块输出的。

注意,MYLIBAPI标志要放在头文件中要输出的变量的定义之前和要输出的函数前面。

MYLIBAPI标志包含了extern “C” 修改符作用是:

  • 使用C风格导出函数

注意:
只有当你编写C++代码而不是直接编写C代码时,才能使用这个修改符。
C + +编译器可能会改变函数和变量的名字,extern “C”,就可以告诉编译器不要改变变量名或函数名,这样,变量和函数就可以供使用 C、C + +或任何其他编程语言编写的可执行模块来访问。

补充:
c++编译器允许同名函数(重载)
c编译器不允许重载。
例如:void foo(int n,int m);
void foo(int n);
C++导出成_foo_int_int和_foo_int,链接器读入时不会报错。
C都导出成_foo_;导致链接会提示函数重命名;

3.1 输出的真正含义是什么

查看dll的输出节

dumpbin.exe -exports xxx.dll

[笔记]Windows核心编程《十九》DLL基础

[笔记]Windows核心编程《十九》DLL基础

RVA:这一列下面的数字用于指明在 D L L文件映像中的什么位置能够找到输出符号的位移量。
hint(提示码):列可供系统用来改进代码的运行性能,在此并不重要。

3.2 创建用于非Visual C++工具的D L L

当使用同一个编译器供应商的工具,创建DLL是不需要做额外工作的。
当使用不同供应商的工具时,则需要做一些额外工作。

调用规则 __stdcall(WINAPI)

当使用 __stdcall将C函数输出时,Microsoft的编译器就会改变函数的名字,设置一个前导下划线,再加上一个 @符号的前缀,后随一个数字,表示作为参数传递给函数的字节数。

例如,下面的函数是作为DLL的输出节中的_MyFunc@8输出的:

_declspec(dllexport) LONG __stdcall MyFunc(int a. int b);

其他编译器工具创建exe时 使用Microfost编译器创建DLL需要注意

若要使用与其他编译器供应商的工具链接的 M i c r o s o f t的工具创建一个可执行模块,必须告诉Microsoft的编译器输出没有经过改变的函数名。
有两种方法:

  • 第一种方法是为编程项目建立一个.def文件,并在该.def文件中加上类似下面的EXPORTS节:
EXPORTS
	MyFunc # 输出函数的最终名
  • 如果想避免使用.def文件,可以使用第二种方法输出未截断的函数版本。在 D L L的源代码模块中,可以添加下面这行代码:
#pragma comment(1inker,"/export:MyFunc=_MyFunc@8")

这行代码使得编译器发出一个链接程序指令,告诉链接程序,一个名叫MyFunc的函数将被输出,其进入点与称为_MyFunc@8的函数的进入点相同。

第二种方法没有第一种方法容易,因为你必须自己截断函数名,以便创建该代码行。
另外,当使用第二种方法时,DLL实际上输出用于标识单个函数的两个符号,即 MyFunc和_MyFunc@8,而第一种方法只输出符MyFunc。第二种方法并没有给你带来更多的好处,它只是使你可以避免使用 .def的文件而已。

四、创建可执行模块

查看输入节

Visual Studio的DumpBin . e x e实用程序(带有- i m p o r t s开关),能够看到模块的输入节的样子。下面是 Calc.exe文件的输入节的一个代码段。

C:\WINNT\SYSTEM32>DUMPBIN -imports Calc.EXE
Dump of file C:\Windows\System32\calc.exe

File Type: EXECUTABLE IMAGE

  Section contains the following imports:

    SHELL32.dll
             1400021B0 Import Address Table
             1400028C0 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                         1AE ShellExecuteW

    KERNEL32.dll
             140002148 Import Address Table
             140002858 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                         224 GetCurrentThreadId
                         2F2 GetSystemTimeAsFileTime
                         310 GetTickCount
                         4D4 RtlCaptureContext
                         220 GetCurrentProcessId
                         4E2 RtlVirtualUnwind
                         5BE UnhandledExceptionFilter
                         57D SetUnhandledExceptionFilter
                         21F GetCurrentProcess
                         59C TerminateProcess
                         451 QueryPerformanceCounter
                         4DB RtlLookupFunctionEntry

    msvcrt.dll
             1400021F0 Import Address Table
             140002900 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                          90 __setusermatherr
                         17D _initterm
                          57 __C_specific_handler
                         382 _wcmdln
                         127 _fmode
                          D2 _commode
                          2F ?terminate@@YAXXZ
                          C1 _cexit
                          9D __wgetmainargs
                          AE _amsg_exit
                          55 _XcptFilter
                         432 exit
                          8E __set_app_type
                         10E _exit

    ADVAPI32.dll
             140002128 Import Address Table
             140002838 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                         122 EventSetInformation
                         129 EventWriteTransfer
                         121 EventRegister

    api-ms-win-core-synch-l1-2-0.dll
             1400021E0 Import Address Table
             1400028F0 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                          2D Sleep

    api-ms-win-core-processthreads-l1-1-0.dll
             1400021D0 Import Address Table
             1400028E0 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                          20 GetStartupInfoW

    api-ms-win-core-libraryloader-l1-2-0.dll
             1400021C0 Import Address Table
             1400028D0 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                          14 GetModuleHandleW

  Summary

        1000 .data
        1000 .pdata
        1000 .rdata
        1000 .reloc
        5000 .rsrc
        1000 .text

,这些 DLL是SHELL32.dll、
msvcrt.dll、ADVAPI32.dll、KERNEL32.dll、GDI32 . dll和User32.dll。
(我的运行的是win10 会有些dll依赖的区别)

五、运行可执行模块

当一个可执行文件被启动时,操作系统加载程序将为该进程创建虚拟地址空间。然后,加载程序将可执行模块映射到进程的地址空间中。加载程序查看可执行模块的输入节,并设法找出任何需要的DLL,并将它们映射到进程的地址空间中。

搜索DLL顺序是:

  1. 包含可执行映像文件exe的目录。
  2. 进程的当前目录。
  3. Windows系统目录。
  4. Windows目录。
  5. PATH环境变量中列出的各个目录。

应该知道其他的东西也会影响加载程序对一个 DLL的搜索:

  • 当D L L模块映射到进程的地址空间中时,加载程序要检查每个 D L L的输入节。如果存在输入节(通常它确实是存在的),那么加载程序便继续将其他必要的 DLL模块映射到进程的地址空间中。加载程序将保持对 DLL模块的跟踪,使模块的加载和映射只进行一次(尽管多个模块需要该模块)。

总结

1.为什么静态C/C++ 运行时库(MT)被其他模块释放内存 会报错?

为什么MT模块内存不能相互释放,而MD的却可以
简单讲,MT库编译时各会包含libcmt.lib,然后每个MT库都有一个独立的堆。一个堆的地址到另一个堆的同名地址去释放,自然会有非法访问问题。文章来源地址https://www.toymoban.com/news/detail-442536.html

2.__stdcall、__cdecl、extern "C"区别 和使用场景。

1. __stdcall:
__stdcall是一种函数调用约定(calling convention),用于指定函数的参数传递和堆栈清除方式。
__stdcall约定要求被调用函数从堆栈中清除其参数,并且函数的参数是从右往左依次入栈。
__stdcall常用于Windows API函数,如LoadLibrary和MessageBox。
在使用__stdcall约定声明函数时,可以使用__declspec(dllexport)或__declspec(dllimport)来指定函数的导出或导入。
2. __cdecl:
__cdecl也是一种函数调用约定,但与__stdcall不同,它要求调用者清除堆栈,并且函数的参数是从左往右依次入栈。
默认情况下,C++函数都是按照__cdecl约定进行调用的,可以省略__cdecl宏定义。
__cdecl约定在函数的定义和声明中都可以使用。
extern "C"3. extern "C"是用于指定C语言风格的函数名和参数名的链接约定。
C++编译器对函数和变量进行了名称修饰(name mangling),以支持函数重载和命名空间等特性,而C语言没有支持这些特性,所以需要使用extern "C"来取消C++的名称修饰。
C语言和C++语言的函数之间可以通过extern "C"进行链接,以实现互相调用。

使用场景:
- 如果你在使用Windows API函数,应该注意使用__stdcall约定声明和调用这些函数。
- 如果你定义的函数需要与其他C语言或第三方库进行交互,可以使用extern "C"来取消名称修饰,确保链接成功。
- 对于一般情况下的函数声明和定义,默认使用__cdecl约定即可,不需要额外指定。

到了这里,关于[笔记]Windows核心编程《十九》DLL基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 并发编程系列---【线程池七大核心参数】

    一、七大核心参数 1.corePoolSize                            核心线程数 2.maximumPoolSize                   最大线程池参数 3.keepAliveTime                         任务结束后,线程存活此处指定时间后才会被释放 4.TimeUnit                                  上一个参数的

    2024年02月13日
    浏览(34)
  • 《Windows核心编程》若干知识点实战应用分享

    目录 1、进程的虚拟内存分区与小于0x10000的小地址内存区 1.1、进程的虚拟内存分区 1.2、小于0x10000的小地址内存区 2、保存线程上下文的CONTEXT结构体 3、从汇编代码角度去理解多线程运行过程的典型实例 4、调用TerminateThread强制结束线程会导致线程中的资源没有释放的问题 5、

    2024年01月25日
    浏览(35)
  • 《Windows核心编程》若干知识点应用实战分享

    目录 1、进程的虚拟内存分区与小于0x10000的小地址内存区 1.1、进程的虚拟内存分区 1.2、小于0x10000的小地址内存区 2、保存线程上下文的CONTEXT结构体 3、从汇编代码角度去理解多线程运行过程的典型实例 4、调用TerminateThread强制结束线程会导致线程中的资源没有释放的问题 5、

    2024年01月22日
    浏览(37)
  • Flutter系列文章-Flutter基础

    Flutter是Google推出的一种新的移动应用开发框架,允许开发者使用一套代码库同时开发Android和iOS应用。它的设计理念、框架结构、以及对Widget的使用,都让开发者能更有效率地创建高质量的应用。 Flutter的设计理念是“一切皆为Widget”。这意味着不论是按钮、字体、颜色、布局

    2024年02月16日
    浏览(34)
  • DevOps系列文章 之 Python基础

    1.定义 1、集合是一个无序的,不重复的数据组合,它的主要作用如下: 1.去重,把一个列表变成集合,就自动去重了 2.关系测试,测试两组数据之前的交集、差集、并集等关系 2、集合类型 1.数学上,把set称做由不同的元素组成的集合,集合(set)的成员通常被称做集合元素

    2024年02月11日
    浏览(29)
  • DevOps系列文章 之 Python 基础

    考点:字符串:str( )、整型数:int( )、浮点数:float( ) 考点:条件分支 考点:引入随机数 考点:while循环语句 考点:for循环语句 Python语言支持以下类型的运算符 以下假设变量a为10,变量b为20: 运算符 描述 实例 + 加 - 两个对象相加 a + b 输出结果 30 - 减 - 得到负数或是一个

    2024年02月11日
    浏览(34)
  • Java基础/进阶/电商系统实战系列文章汇总

    目录 📢前言 ✨ 专栏介绍 ✨ 专栏特色​ ✨ 适合人群 

    2024年02月02日
    浏览(32)
  • C++核心编程【快速拾起来】【复习学习笔记】

    本阶段主要针对C++ 面向对象 编程技术做详细讲解,探讨C++中的核心和精髓。 C++程序在执行时,将内存大方向划分为 4个区域 代码区:存放函数体的二进制代码,由操作系统进行管理的 全局区:存放全局变量和静态变量以及常量 栈区:由编译器自动分配释放, 存放函数的参数

    2024年02月12日
    浏览(24)
  • Windows核心编程(第五版)_1_错误处理_字符处理_内核对象

    ʕ •ᴥ•ʔ ɔ: 调用Windows函数时,它会先验证我们传给它的参数,然后再开始执行任务。如果传入的参数无效,或者由于其他原因导致操作无法执行,则函数的返回值将指出函数因为某些原因失败了。表1-1展示了大多数Windows函数使用的返回值的数据类型。 1.1 函数的错误码 通

    2024年02月08日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包