C# 与 Windows API 交互的“秘密武器”:结构体和联合体
一、引言
在 C# 的编程世界里,当我们想要深入挖掘 Windows 系统的底层功能,与 Windows API 打交道时,结构体和联合体就像是两把神奇的钥匙🔑 它们能够帮助我们精准地操控数据,实现一些高级且强大的功能。就好比搭建一座精密的机器,每个零件都必须严丝合缝,结构体和联合体就是这些关键零件,确保我们与 Windows API 的交互顺畅无阻。今天,咱们就一起揭开它们神秘的面纱,看看在 C# 调用 Windows API 的过程中,它们究竟有着怎样的魔力。
二、结构体和联合体是什么?
(一)结构体的定义与特性
在 C# 中,结构体(Struct)是一种值类型,这意味着它在内存中有着独特的存储方式,与引用类型不同,值类型变量直接存储其数据,而不是存储对数据的引用。结构体就像是一个精致的收纳盒,能够将多个不同类型或者相同类型的数据成员有序地封装在一起。比如说,当我们在处理图形相关的编程任务时,常常需要表示一个点的坐标,这时就可以定义一个结构体:
// 定义一个结构体,用于存储点的坐标
public struct Point
{public int X;public int Y;public Point(int x, int y){X = x;Y = y;}
}
在上述代码中,我们清晰地定义了 Point 结构体,它包含了两个整型数据成员 X 和 Y,分别用于表示点在二维平面中的横坐标与纵坐标。结构体中的构造函数则为我们提供了便捷的初始化方式,就像给收纳盒里的物品设定初始摆放位置一样。在 Main 方法中,我们可以轻松地创建一个 Point 结构体的实例:
class Program
{static void Main(){// 创建一个点结构体实例Point p = new Point(10, 20);Console.WriteLine($"点P的坐标是: ({p.X}, {p.Y})");}
}
通过这段代码,我们将坐标点 (10, 20) 封装在 p 实例中,并且能够准确无误地将其坐标信息打印输出,是不是非常直观且方便呢?结构体这种能够将相关数据紧密捆绑在一起的特性,使得我们在处理复杂数据组合时,代码逻辑更加清晰,数据管理更加高效。
(二)联合体的独特之处
联合体(Union),从名字上看就感觉它有着特殊的魔力,它其实是一种特殊的结构体。想象一下,在同一个小小的内存空间里,它可以像一位神奇的换装大师,随时变换存储的数据类型。在 C 或 C++ 中,联合体有直接的语法支持,而在 C# 中,虽然没有原生的联合体关键字,但我们可以巧妙地借助结构体和显式布局来模拟出它的行为。比如说:
using System;
using System.Runtime.InteropServices;
// 定义一个模拟联合体的结构体
[StructLayout(LayoutKind.Explicit)]
public struct UnionExample
{[FieldOffset(0)]public int IntegerValue;[FieldOffset(0)]public double DoubleValue;
}
在这段代码中,我们通过 StructLayout(LayoutKind.Explicit) 属性精心规划了结构体的内存布局,让它按照我们期望的方式排列。再利用 FieldOffset(0) 属性,指明 IntegerValue 和 DoubleValue 这两个成员都从内存的起始位置(偏移量为 0)开始存储,这就意味着它们共享同一块内存区域。
接着,在 Main 方法中,我们来看看它的神奇表现:
class Program
{static void Main(){// 创建一个联合体实例,并设置整数值UnionExample u = new UnionExample();u.IntegerValue = 123;// 打印整数值Console.WriteLine($"整数值: {u.IntegerValue}");// 设置双精度浮点数值u.DoubleValue = 123.456;// 打印双精度浮点数值Console.WriteLine($"双精度浮点数值: {u.DoubleValue}");}
}
当我们先给 IntegerValue 赋值为 123 并打印时,一切都很正常。但当我们紧接着给 DoubleValue 赋值为 123.456 后,再去打印 IntegerValue,就会发现它的值已经发生了变化,不再是原来的 123。这是因为它们共享同一块内存,新的数据覆盖了旧的数据,就如同换装大师穿上了新衣服,旧衣服自然就看不到了。这种特性使得联合体在某些特定场景下,能够极大地节省内存空间,当我们明确知道在不同时刻只需要使用其中一种数据类型时,联合体就能发挥它的超能力。与结构体相比,结构体的每个成员都有自己独立的内存空间,数据相互独立,互不干扰;而联合体则是以牺牲数据的同时存在性为代价,换来内存利用的高效性,在不同的编程需求下,它们各自有着不可替代的优势。
三、结构体的魔法
(一)在 C# 中的定义和使用方式
在 C# 中定义结构体,就如同打造一个专属的工具盒。我们使用 struct 关键字开启结构体的定义之旅,在大括号内精心排列各种数据成员,这些成员可以是基本数据类型,如 int、double、string 等,也能是其他已定义的结构体类型,就像在收纳盒里放置不同种类的小物件。例如:
// 定义一个表示书籍信息的结构体
public struct Book
{public string Title;public string Author;public int PageCount;public Book(string title, string author, int pageCount){Title = title;Author = author;PageCount = pageCount;}
}
在这段代码里,Book 结构体如同一个精致的书籍档案夹,它收纳了书籍的标题 Title、作者 Author 以及页数 PageCount 这些关键信息,构造函数则为快速整理书籍档案提供了便捷方式。当我们在 Main 方法中使用它时:
class Program
{static void Main(){// 创建一个Book结构体实例Book myBook = new Book("《C#编程探秘》", "神秘博主", 300);Console.WriteLine($"书名:{myBook.Title},作者:{myBook.Author},页数:{myBook.PageCount}");}
}
通过实例化 Book 结构体,我们轻松地将一本虚拟书籍的信息封装其中,并准确地将这些信息展示出来,是不是感觉结构体就像是我们手中灵活的数据整理小助手呢?而且,结构体作为值类型,在赋值操作时会进行数据的完整拷贝,这意味着改变一个结构体实例的成员值,不会影响到其他副本,就像复制了多个一模一样的收纳盒,修改其中一个盒子里的物品,不会影响到其他盒子。
(二)实际应用场景举例
结构体在实际编程中的应用场景十分广泛,尤其是在与 Windows API 交互时,它常常扮演着数据传递使者的重要角色。在图形处理领域,当我们需要绘制图形、处理图像坐标或者判断图形之间的位置关系时,结构体就大显身手了。就拿前面提到的 Point 结构体来说,它可以精准地表示二维平面上的一个点的坐标,无论是绘制简单的线条、矩形,还是复杂的多边形,都离不开它对坐标点的准确描述。在绘制一个矩形时,我们可能会定义如下结构体:
[StructLayout(LayoutKind.Sequential)]
public struct Rectangle
{public Point TopLeft;public Point BottomRight;
}
这里的 Rectangle 结构体巧妙地利用了 Point 结构体,将矩形的左上角和右下角坐标封装起来,使得在图形绘制函数中传递矩形信息变得简洁明了。当调用绘制矩形的 Windows API 函数时,只需将 Rectangle 结构体实例作为参数传递,函数就能精准地获取矩形的位置和大小信息,在屏幕上绘制出我们期望的图形。
在系统信息获取方面,结构体同样不可或缺。比如,当我们想要获取系统的内存状态、CPU 信息或者窗口相关属性时,Windows API 提供了相应的函数,而这些函数往往要求以特定结构体作为参数来接收返回的数据。以获取系统内存状态为例,可能会有类似这样的结构体:
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_INFO
{public uint dwLength;public uint dwMemoryLoad;public uint dwTotalPhys;public uint dwAvailPhys;public uint dwTotalPageFile;public uint dwAvailPageFile;public uint dwTotalVirtual;public uint dwAvailVirtual;
}
通过调用相关的 Windows API 函数,并传入 MEMORY_INFO 结构体实例,函数就能将系统内存的各种详细信息填充到结构体的各个成员中,我们后续只需在 C# 代码中读取这些成员值,就能清晰地了解系统内存的使用情况,就像是拥有了一个系统内存的信息仪表盘,为优化系统性能提供精准的数据支持。
四、联合体的超能力
(一)C# 中模拟联合体的方法
在 C# 中,虽然没有像 C 或 C++ 那样直接使用 union 关键字来定义联合体,但我们拥有强大的.NET 框架特性,能够巧妙地模拟出联合体的神奇功能。关键就在于 StructLayout 和 FieldOffset 这两个属性。StructLayout 属性如同一位精密的建筑师,它精心规划结构体在内存中的布局方式。当我们将其设置为 LayoutKind.Explicit 时,就相当于告诉编译器:“嘿,我要按照自己的精确规划来安排结构体成员的内存位置。” 而 FieldOffset 属性则像是一个个精准的坐标标记,它指明了每个结构体成员在内存中的具体偏移量。
就拿之前提到的代码为例:
using System;
using System.Runtime.InteropServices;
// 定义一个模拟联合体的结构体
[StructLayout(LayoutKind.Explicit)]
public struct UnionExample
{[FieldOffset(0)]public int IntegerValue;[FieldOffset(0)]public double DoubleValue;
}
在这段代码里,[StructLayout(LayoutKind.Explicit)] 为 UnionExample 结构体设定了独特的内存布局规则,使其成员的存储打破常规。[FieldOffset(0)] 这个标记对于 IntegerValue 和 DoubleValue 来说,就如同将它们都精确地放置在内存的起始起跑线,意味着它们共享同一块初始内存区域。这就模拟出了联合体那种在同一内存位置存储不同数据类型的特性。当我们在 Main 方法中操作这个模拟联合体时:
class Program
{static void Main(){// 创建一个联合体实例,并设置整数值UnionExample u = new UnionExample();u.IntegerValue = 123;// 打印整数值Console.WriteLine($"整数值: {u.IntegerValue}");// 设置双精度浮点数值u.DoubleValue = 123.456;// 打印双精度浮点数值Console.WriteLine($"双精度浮点数值: {u.DoubleValue}");}
}
可以清晰地看到,先给 IntegerValue 赋值为 123 并打印,一切顺利。但当紧接着给 DoubleValue 赋值为 123.456 后,再次打印 IntegerValue,它的值已经被新的数据无情覆盖,不再是最初的 123。这正是联合体内存共享特性的生动体现,在 C# 中通过这样巧妙的模拟,我们同样能够驾驭这种强大的数据存储方式。
(二)应用优势与注意事项
联合体在实际应用中有着诸多独特的优势。最显著的一点就是它能够极大地节省内存空间。想象一下,在某些特定的场景下,比如处理一些传感器数据,我们可能在不同时刻只需要用到整数类型或者浮点类型来表示数据,这时使用联合体,让它们共享同一块内存,就避免了像结构体那样为每个成员开辟独立内存空间,从而有效减少内存的占用。就如同在一个狭小的储物箱里,通过合理安排物品的存放方式,让不同的物品在不同时间共用同一个空间,达到空间利用的最大化。
在与一些底层硬件交互或者解析特定格式的二进制数据时,联合体也能发挥关键作用。例如,在网络编程中接收数据包,数据包的头部可能根据不同的协议版本,某个字段既可以是整数表示的简单标识,也可以是复杂结构体表示的详细信息。这时,联合体就能让我们轻松应对这种数据格式的变化,根据实际情况灵活地将同一块内存区域解读为不同的数据类型,高效地解析数据包,确保数据传输的准确与流畅。
不过,使用联合体也需要格外小心,如同在钢丝上行走,稍有不慎就可能引发问题。由于成员共享同一块内存,数据覆盖的风险时刻存在。就像前面的例子,给一个成员赋值后,再给另一个共享内存的成员赋值,之前的值就会被覆盖丢失。所以在代码编写过程中,必须时刻牢记当前联合体中存储的数据类型,精准地操作对应的成员,避免出现数据混乱的情况。而且,这种内存共享的特性也使得联合体的代码可读性相对较差,对于后续维护代码的开发者来说,可能需要花费更多的精力去理解其中的数据流向和逻辑关系。因此,在使用联合体时,权衡好内存优化与代码可维护性之间的平衡至关重要,确保它在正确的场景下发挥最大的效能,而不是成为引发程序错误的 “定时炸弹”。
五、结构体和联合体在 Windows API 中的应用
(一)重要性体现
在与 Windows API 交互的过程中,结构体和联合体的重要性怎么强调都不为过。Windows API 作为 Windows 系统的底层接口,函数繁多且功能各异,它们就像一个个精密的工具,等待着我们用合适的数据去驱动。而结构体和联合体,恰恰就是构建这些精准数据的基石。许多 Windows API 函数需要特定格式的数据才能正常工作,结构体和联合体能够完美地将数据按照要求进行封装,以正确的格式传递给 API 函数,确保程序与 Windows API 之间的通信顺畅无阻,就如同为不同形状的拼图找到了恰好匹配的空位,让整个画面得以完整呈现。
比如说,当我们想要获取系统中某个窗口的详细信息时,Windows API 提供了 GetWindowInfo 这样的函数,但它要求传入一个特定结构体来接收返回的数据。这个结构体必须精确地定义各个成员的类型和顺序,以匹配 API 底层的期望。如果没有结构体来整齐划一地整理这些数据,我们就像是将一堆杂乱无章的零件丢给一个精密仪器,仪器根本无法正常运转,程序自然也就无法获取到准确的窗口信息,更别提后续的处理与操作了。
(二)具体实例剖析
让我们深入剖析一个获取窗口信息的实例,看看结构体是如何在其中发挥关键作用的。首先,我们定义了如下结构体:
using System;
using System.Runtime.InteropServices;
// 定义一个结构体,用于存储窗口信息
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWINFO
{public uint cbSize;public RECT rcWindow;public RECT rcClient;public uint dwStyle;public uint dwExStyle;public uint dwWindowStatus;public uint cxWindowBorders;public uint cyWindowBorders;public ushort atomWindowType;public ushort wCreatorVersion;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{public int left;public int top;public int right;public int bottom;
}
在这段代码中,WINDOWINFO 结构体宛如一个精心设计的窗口信息收纳盒,它的每个成员都各司其职。cbSize 成员就像是一个标识,告诉 API 函数这个结构体的大小,确保数据传输的准确性;rcWindow 和 rcClient 成员则通过嵌套的 RECT 结构体,精确地描述了窗口的整体矩形区域以及客户区矩形区域,从窗口的边界位置到内部可用区域的范围都能精准定位;其他成员如 dwStyle、dwExStyle 等,分别对应着窗口的样式、扩展样式以及各种状态信息,涵盖了窗口外观、行为等多方面的特征。
当我们在 Main 方法中调用 GetWindowInfo 函数时:
class Program
{[DllImport("user32.dll")]public static extern bool GetWindowInfo(IntPtr hWnd, ref WINDOWINFO pwi);static void Main(){// 假设我们已经获取到了目标窗口的句柄,这里用hwnd表示IntPtr hwnd = /* 获取窗口句柄的代码 */; WINDOWINFO windowInfo = new WINDOWINFO();windowInfo.cbSize = (uint)Marshal.SizeOf(windowInfo);bool result = GetWindowInfo(hwnd, ref windowInfo);if (result){Console.WriteLine($"窗口位置:({windowInfo.rcWindow.left}, {windowInfo.rcWindow.top}) - ({windowInfo.rcWindow.right}, {windowInfo.rcWindow.bottom})");Console.WriteLine($"客户区位置:({windowInfo.rcClient.left}, {windowInfo.rcClient.top}) - ({windowInfo.rcClient.right}, {windowInfo.rcClient.bottom})");Console.WriteLine($"窗口样式:{windowInfo.dwStyle}");// 可以继续输出其他成员信息}}
}
在上述代码中,首先我们通过 DllImport 引入 GetWindowInfo 函数,确保能够在 C# 代码中调用这个 Windows API 函数。接着,在 Main 方法里,我们假设有了目标窗口的句柄(实际应用中需要通过其他方式获取,如 FindWindow 函数等),创建了 WINDOWINFO 结构体实例并初始化 cbSize 成员,这一步至关重要,它让 API 函数知道要接收的数据结构大小。当调用 GetWindowInfo 函数后,如果返回值为 true,表示成功获取到窗口信息,此时我们就能像打开装满宝藏的箱子一样,从结构体的各个成员中取出窗口的详细信息,无论是窗口在屏幕上的坐标位置、客户区的范围,还是窗口的样式特征,都能一目了然,为后续对窗口的进一步操作,如移动、缩放、修改样式等提供了坚实的数据基础,让我们的程序能够与 Windows 系统的窗口进行深度交互,实现更加丰富多样的功能。
六、常见问题与解决策略
(一)定义和使用中的错误类型
在使用结构体和联合体与 Windows API 交互的过程中,我们就像在布满礁石的航道上航行,一不小心就可能触礁。常见的错误类型还真不少,比如说结构体成员类型不匹配,就像拿着错误尺寸的拼图碎片,怎么也塞不进对应的空位。当我们定义的结构体成员类型与 Windows API 要求的不一致时,可能导致数据传输错误,进而引发程序崩溃或者得到错误的结果。比如在调用一个获取网络数据包信息的 Windows API 函数时,结构体中某个表示数据包长度的成员,在 C# 中错误地定义为 int 类型,而 API 要求的是 uint 类型,这就可能导致读取数据包长度时出现负值或者溢出的情况,使得后续对数据包的解析完全错乱。
联合体内存布局错误也是一个容易让人掉进的 “陷阱”。由于联合体成员共享同一块内存,如果在定义联合体时,没有正确设置 FieldOffset 等属性,或者不小心改变了成员的顺序、大小,就会像打乱了房间里家具的摆放,导致数据存储和读取混乱。例如,在一个模拟硬件设备状态的联合体中,一个成员用于表示设备的开关状态(bool 类型),另一个成员用于表示设备的温度值(float 类型),如果错误地设置了偏移量,可能会出现读取开关状态时得到的却是温度值的一部分二进制数据,将其错误解读为开关状态,这显然会引发严重的逻辑错误。
还有结构体或联合体在跨平台使用时的兼容性问题,就如同在不同规格的轨道上行驶的火车,容易脱轨。不同的操作系统或者硬件平台,对数据的存储方式、字节对齐等可能存在差异。在 32 位系统上正常运行的结构体代码,移植到 64 位系统时,可能因为指针类型的长度变化、结构体的填充字节数不同等原因,导致数据错位,程序出现莫名其妙的错误。这就要求我们在编写代码时,要有前瞻性,充分考虑到可能的平台差异,使用合适的属性(如 StructLayout 的不同布局选项)来确保结构体和联合体在不同平台上都能稳定运行。
(二)调试技巧与工具推荐
当遇到这些棘手的问题时,别慌,我们有一些调试的 “秘密武器”。首先,善用 Visual Studio 的调试功能,它就像是我们的程序侦探。在调试模式下,可以逐步执行代码,观察结构体和联合体变量在每一步的变化,查看内存中的数据存储情况。通过设置断点,在关键代码行暂停执行,仔细检查变量的值是否符合预期。比如在调用 Windows API 函数前后,分别查看作为参数传递的结构体成员值,看是否在函数调用过程中被正确赋值或者修改。还可以利用监视窗口,实时跟踪结构体和联合体中各个成员的数值变化,一旦发现异常,就能迅速定位到问题代码所在。
另外,有一个非常实用的工具 ——P/Invoke Interop Assistant,它像是一位贴心的导航员,能帮助我们在复杂的 P/Invoke 调用中找到正确的方向。当我们对结构体或函数的声明不确定时,只需将 Native 函数或者结构的声明拷贝到工具的相应文本框,点击生成,就能获取对应的.NET 声明,避免了手动声明时容易出现的错误。而且它还能查找 Win32 API 在.NET 中的声明,验证我们编写的.NET 函数或结构在 C 中的声明是否正确,为跨语言调用保驾护航,大大提高了我们编写代码的效率和准确性,让我们在与 Windows API 交互的编程之旅中更加顺畅。
七、总结
通过这一趟深入的探索之旅,我们清晰地认识到结构体和联合体在 C# 与 Windows API 交互中无可替代的关键作用。结构体宛如一位严谨的数据管家,有条不紊地将各类相关数据整理打包,以精准无误的格式递交给 Windows API,确保信息传递的准确性,让我们的程序能够顺利获取系统资源、操控窗口等诸多强大功能。联合体则像一位神奇的空间魔法师,在特定场景下,通过巧妙共享内存,为我们节省宝贵的内存资源,特别是在处理一些对内存占用敏感、数据类型需灵活切换的任务时,展现出独特的优势。
然而,这一路上我们也看到了不少隐藏在暗处的 “礁石”,从结构体成员类型的匹配失误,到联合体内存布局的错乱,再到跨平台时兼容性的挑战,稍有不慎就可能让程序 “触礁”。但别怕,我们手握 Visual Studio 调试利器以及 P/Invoke Interop Assistant 导航仪,只要善加利用,就能在遇到问题时迅速找准方向,化险为夷。
希望大家在今后的编程实践中,大胆地运用结构体和联合体这两把神奇钥匙,开启 Windows API 的强大宝库,去探索更多未知的编程天地,让你的程序绽放出别样的光彩,轻松应对各种复杂的开发需求,向着编程高手之路大步迈进!
相关文章:
C# 与 Windows API 交互的“秘密武器”:结构体和联合体
一、引言 在 C# 的编程世界里,当我们想要深入挖掘 Windows 系统的底层功能,与 Windows API 打交道时,结构体和联合体就像是两把神奇的钥匙🔑 它们能够帮助我们精准地操控数据,实现一些高级且强大的功能。就好比搭建一…...
【数字化】华为-用变革的方法确保规划落地
导读:华为在数字化转型过程中,深刻认识到变革的必要性,并采用了一系列有效的方法确保转型规划的有效落地。华为认为,数字化转型不仅仅是技术层面的革新,更是企业运作模式、流程、组织、文化等深层次的变革。数字化转型…...
SpringData-Redis缓存
Spring Framework是领先的全堆栈Java/JEE应用程序框架。它提供了一个轻量级容器和一个通过使用依赖注入、AOP和可移植服务抽象实现的非侵入性编程模型。 NoSQL存储系统为传统RDBMS提供了一种横向可扩展性和速度的替代方案。就实现而言,键值存储代表NoSQL空间中最大…...
大语言模型兵马未动,数据准备粮草先行
从OpenAI正式发布ChatGPT开始,大型语言模型(LLM)就变得风靡一时。对业界和吃瓜群众来说,这种技术最大的吸引力来自于理解、解释和生成人类语言的能力,毕竟这曾被认为是人类独有的技能。类似CoPilot这样的工具正在迅速…...
跳表和Mysql联合索引的最左原则和索引下推的优化
文章目录 跳表(Skip List)关键特性跳表的结构示意图跳表的查询效率为什么 MySQL 不使用跳表而使用 B 树?跳表的实际应用场景 总结 MySQL 联合索引的最左匹配原则最左匹配原则的规则示例:创建联合索引查询示例及索引使用情况设计联…...
Android切换语言不退出App
1.需求 实现用户选择语言(未点击下一步),更新当前界面UI,点击下一步后,更新App的语言,并进行保存。 实现目标: 1.设置App的语言,本地进行保存 2.updateResources更新本地语言配置…...
Unity编程与游戏开发-编程与游戏开发的关系
游戏开发是一个复杂的多领域合作过程,涵盖了从创意构思到最终实现的多个方面。在这个过程中,技术、设计与美术三大核心要素相互交织,缺一不可。在游戏开发的过程中,Unity作为一款强大的跨平台游戏引擎,凭借其高效的开发工具和庞大的社区支持,成为了很多游戏开发者的首选工…...
【Git】问题汇总
在push的时候显示 protocol error: bad line length 8192 我在本地创建了一个gogs服务器,现在正在上传代码,但是出现了上述的这个问题。 解决方法 设置本地http.postBuffer(待验证) 方法一:全局配置 git config --g…...
搭建docker私有化仓库Harbor
Docker私有仓库概述 Docker私有仓库介绍 Docker私有仓库是个人、组织或企业内部用于存储和管理Docker镜像的存储库。Docker默认会有一个公共的仓库Docker Hub,而与Docker Hub不同,私有仓库是受限访问的,只有授权用户才能够上传、下载和管理其中的镜像。这种私有仓库可以部…...
修改sshd默认配置,提升安全
对于Linux服务器,特别是暴露在公网的服务器,会经常被人扫描、探测和攻击。包括通过ssh访问登录攻击。对此,对默认的sshd配置进行调整,提升安全。 下面以CentOS 7.9为例说明: 一、常见安全措施 以root用户编辑vim /e…...
formik 的使用
礼记有言:独学而无友,则孤陋而寡闻 让我们一起了解更多便捷方法,缩短开发时间去摸鱼,嘿嘿。 框架:react 在写表单的时候,我不太喜欢把验证写的很繁琐,这里讲介绍,验证表单的非常好用…...
【学习笔记】理解深度学习的基础:机器学习
1. 机器学习基础 1.1 机器学习的定义与重要性 定义:深度学习是机器学习的一种特定形式。为了深入理解深度学习,必须牢固掌握机器学习的基本原理。机器学习算法是一种能够从数据中学习的算法,通过经验E在任务T上提高性能度量P(Mi…...
Docker 基础知识
背景 传统的linux的环境部署 命令多步骤多安装版本多使用docker的话,一个命令就可以全部搞定安装linux 之前安装过,所以直接使用的开罩进行复制的如果之前配置过静态地址,需要改成IPV4静态地址访问安装docker 参考连接:https://b11et3un53m.feishu.cn/wiki/Rfocw7ctXij2RBk…...
pyqt鸟瞰
QApplication是Qt框架中的一个类,专门用于管理基于QWidget的图形用户界面(GUI)应用程序的控制流和主要设置。QApplication类继承自QGuiApplication,提供了许多与GUI相关的功能,如窗口系统集成、事件处理等。 QAppli…...
Linux syslog 运行机制
Busybox的syslogd认识与使用 syslogd 的基本工作原理: syslogd 是一个系统日志守护进程,它接收来自各种进程和系统服务的日志消息,并根据配置将这些消息存储到不同的日志文件中。 syslogd日志记录器由两个守护进程(klogd&#x…...
ZYNQ初识10(zynq_7010)UART通信实验
基于bi站正点原子讲解视频: 系统框图(基于串口的数据回环)如下: 以下,是串口接收端的波形图,系统时钟和波特率时钟不同,为异步时钟,,需要先延时两拍,将时钟同…...
day38 tcp 并发 ,linux下的IO模型----IO多路复用
TCP 并发 由于tcp协议只能实现一对一的通信模式。为了实现一对多,有以下的的处理方式 1. 多进程 开销大 效率低 2. 多线程 创建线程需要耗时 3. 线程池 多线程模型创建线程耗时问题,提前创建 4. IO多路复用 在不创建进程和线程的前提下,对…...
el-date-picker 禁用一个月前、一个月后(当天之后)的时间 datetimerange
文章目录 功能需求今天是 2025-01-09示例1示例2 代码 Vue2 功能需求 时间范围选择器,最大时间选择尺度为一个月。 今天是 2025-01-09 示例1 选择 2025-01-02 日 禁用未来日期(2025-01-09之后日期) 禁用上月2号(31日之前&#…...
ES6的高阶语法特性
一、模板字符串的高级用法 1.1.模板字符串的嵌套 模板字符串的嵌套允许在一个模板字符串内部再嵌入一个或多个模板字符串。这种嵌套结构在处理复杂数据结构或生成具有层级关系的文本时非常有用。 1. 嵌套示例 假设我们有一个包含多个对象的数组,每个对象都有名称、…...
数据在内存的存储
数据类型介绍 前面我们已经学习了基本的内置类型: char //字符数据类型 1字节 打印%c short //短整型 2字节 打印%hd int //整形 4字节 打印%d long long int //长整型 4/8字节 打印%ld l…...
【微服务】面试题 6、分布式事务
分布式事务面试题讲解 一、问题背景与解决方案概述 因微服务项目涉及远程调用可能引发分布式事务问题,需解决。主流解决方案有阿里 Seata 框架(含 XA、AT、TCC 模式)和 MQ。 二、Seata 框架关键角色 事务协调者(TC)&…...
VMware中Ubuntu如何连接网络?安排!
一、设置NAT模式 1、关闭Ubuntu虚拟机: 确保Ubuntu已经完全关机,而不是挂起或休眠状态。 2、编辑虚拟网络设置: 在VMware主界面点击“编辑”菜单,选择“虚拟网络编辑器”。 如果需要,选择VMnet8 (NAT模式)并点击“更改…...
最近在盘gitlab.0.先review了一下docker
# 正文 本猿所在产品的代码是保存到了一个本地gitlab实例上,实例是别的同事搭建的。最近又又又想了解一下,而且已经盘了一些了,所以写写记录一下。因为这个事儿没太多的进度压力,索性写到哪儿算哪儿,只要是新了解到的…...
TCP封装数据帧
void *send_data(void *arg) //这是一个发送数据的线程 {int sockfd init_tcp_cli("192.168.0.148",50000) //传ip和port,port 50000是因为大概前五万都被其它服务所占用,50000后是私人ipif(sockfd < 0){return NULL;}unsigned char …...
基于Springboot+Vue的仓库管理系统
开发一个基于Spring Boot和Vue的仓库管理系统涉及到前端和后端的开发。本文呢,给出一个简单的开发步骤指南,用于指导初入的新手小白如何开始构建这样一个系统,如果**你想直接学习全部内容,可以直接拉到文末哦。** 开始之前呢给小…...
工厂人员定位管理系统方案(二)人员精确定位系统架构设计,适用于工厂智能管理
哈喽~这里是维小帮,提供多个场所的定位管理方案,如需获取工厂人员定位管理系统解决方案可前往文章最下方获取,如有项目合作及技术交流欢迎私信我们哦~撒花 在上一篇文章中,我们初步探讨了工厂人员定位管理系统的需求背景以及定位方…...
机器学习特征重要性之feature_importances_属性与permutation_importance方法
一、feature_importances_属性 在机器学习中,分类和回归算法的 feature_importances_ 属性用于衡量每个特征对模型预测的重要性。这个属性通常在基于树的算法中使用,通过 feature_importances_ 属性,您可以了解哪些特征对模型的预测最为重要…...
Go学习:多重赋值与匿名变量
目录 1. 变量的多重赋值 1.1 基本语法格式 1.2 交换变量值 2. 匿名变量的使用 1. 变量的多重赋值 1.1 基本语法格式 go语言中,可以将多个赋值语句 合并成 一句,比如: a : 10 b : 20 c : 30//a,b,c三个变量的赋值语句可以…...
解读Linux Bridge中的东西流向与南北流向
解读Linux Bridge中的东西流向与南北流向 在现代云计算和虚拟化环境中,网络流量的管理和优化变得越来越重要。Linux Bridge作为Linux内核提供的一个强大的二层交换机工具,在虚拟化和容器化应用中扮演着至关重要的角色。本文将深入探讨Linux Bridge中的两…...
spring mvn 国际化配置
目录 国际化配置测试测试自定义一个MessageSource类型的beanSpringApplicationUtil工具类MessageUtls工具类配置 国际化原理ResourceBundleMessageSource 国际化配置测试 测试 测试: 自定义一个MessageSource类型的bean import org.springframework.context.Mess…...
Windows下Dll在Unity中使用的一般方式
Windows下Dll在Unity中使用的一般方式 Unity中虽然已经有广泛的库和插件,但是相较于C的库生态而言,还是有一定的差距;因此本篇博文记录Windows下将C函数打包成动态链接库在Unity中使用的一般方法。 环境 Visual Studio 2019 , Uni…...
SQLite PRAGMA
SQLite的PRAGMA命令是一种特殊的命令,用于在SQLite环境中控制各种环境变量和状态标志。PRAGMA值可以被读取,也可以根据需求进行设置【0†source】。 PRAGMA命令的语法格式如下: 要查询当前的PRAGMA值,只需提供该PRAGMA的名字&am…...
Linux:进程控制
1.fork()函数初识 在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。 #include <unistd.h> pid_t fork(void); 返回值:自进程中返回0,父进程返回子进程id࿰…...
一些计算机零碎知识随写(25年1月)-1
我原以为世界上有技术的那批人不会那么闲,我错了,被脚本真实了。 今天正隔着画画呢,手机突然弹出几条安全告警通知。 急忙打开服务器,发现问题不简单,直接关服务器重装系统..... 首先,不要认为小网站&…...
spring mvc源码学习笔记之十
前面的文章介绍了用 WebApplicationInitializer 或者 AbstractAnnotationConfigDispatcherServletInitializer 来代替 web.xml 。 我们学 java web 的时候就知道,servlet 容器会自动加载 web.xml。 那么,疑问就来了,WebApplicationInitialize…...
网络安全-安全散列函数,信息摘要SHA-1,MD5原理
安全散列函数 单向散列函数或者安全散列函数之所以重要,不仅在于消息认证(消息摘要。数据指纹)。还有数字签名(加强版的消息认证)和验证数据的完整性。常见的单向散列函数有MD5和SHA 散列函数的要求 散列函数的目的是文件、消息或者其它数据…...
《解锁计算机视觉智慧:编程实现图片场景文字描述的开源宝藏》
《解锁计算机视觉智慧:编程实现图片场景文字描述的开源宝藏》 一、MiniGPT-4:小模型撬动大视觉理解(一)项目概览(二)核心亮点(三)上手体验 二、ClipCap-Chinese:中文场景…...
vue封装axios请求
在vue项目中我们发送请求一般是使用axios 我们可以封装axios来避免冗余代码 首先引入axios npm install axios创建环境配置文件 NODE_ENV development VITE_APP_TITLE dev VITE_APP_BASE_API /test VITE_SERVE "http://127.0.0.1"上面是创建dev配置文件 也可以…...
【前端动效】原生js实现拖拽排课效果
目录 1. 效果展示 2. 效果分析 2.1 关键点 2.2 实现方法 3. 代码实现 3.1 html部分 3.2 css部分 3.3 js部分 3.4 完整代码 4. 总结 1. 效果展示 如图所示,页面左侧有一个包含不同课程(如语文、数学等)的列表,页面右侧…...
Python Selenium库入门使用,图文详细。附网页爬虫、web自动化操作等实战操作。
文章目录 前言1 创建conda环境安装Selenium库2 浏览器驱动下载(以Chrome和Edge为例)3 基础使用(以Chrome为例演示)3.1 与浏览器相关的操作3.1.1 打开/关闭浏览器3.1.2 访问指定域名的网页3.1.3 控制浏览器的窗口大小3.1.4 前进/后…...
AI华佗?港中大、深圳大数据研究院提出医疗推理大模型HuatuoGPT-o1
编辑 | 白菜叶 OpenAI o1 的突破凸显了通过增强推理能力来提高自然语言大模型(LLM)的应用潜力。然而,大多数推理研究都集中在数学任务上,而医学等领域尚未得到充分探索。 医学领域虽然不同于数学,但鉴于医疗保健的高…...
openEuler22.03系统使用Kolla-ansible搭建OpenStack
Kolla-ansible 是一个利用 Ansible 自动化工具来搭建 OpenStack 云平台的开源项目,它通过容器化的方式部署 OpenStack 服务,能够简化安装过程、提高部署效率并增强系统的可维护性。 前置环境准备: 系统:openEuler-22.03-LTS-SP4 配置&…...
uni-app无限级树形组件简单实现
因为项目一些数据需要树形展示,但是官网组件没有。现在简单封装一个组件在app中使用,可以无线嵌套,展开,收缩,获取子节点数据等。 简单效果 组件TreeData <template><view class"tree"><te…...
初学stm32 --- ADC单通道采集
目录 ADC寄存器介绍(F1) ADC控制寄存器 1(ADC_CR1) ADC控制寄存器 2(ADC_CR2) ADC采样时间寄存器1(ADC_SMPR1) ADC采样时间寄存器2(ADC_SMPR2) ADC规则序列寄存器 1(ADC_SQR1) ADC规则序列寄存器 2(ADC_SQR2) ADC规则序列寄存器 3(ADC_SQR3) AD…...
css盒子水平垂直居中
目录 1采用flex弹性布局: 2子绝父相margin:负值: 3.子绝父相margin:auto: 4子绝父相transform: 5通过伪元素 6table布局 7grid弹性布局 文字 水平垂直居中链接:文字水平垂直居中-CSDN博客 以下为盒子…...
django基于Python的智能停车管理系统
1.系统概述 1.定义:Django 基于 Python 的智能停车管理系统是一个利用 Django 框架构建的软件系统,用于高效地管理停车场的各种事务,包括车辆进出记录、车位预订、收费管理等诸多功能。 2.目的:它的主要目的是提高停车场的运营效…...
Rabbit Rocket kafka 怎么实现消息有序消费和延迟消费的
在消息队列系统中,像 RabbitMQ、RocketMQ 和 Kafka 这样的系统,都支持不同的方式来实现消息的有序消费和延迟消费。下面我们分别探讨这些系统中如何实现这两种需求: 1. RabbitMQ:实现消息有序消费和延迟消费 有序消费࿱…...
Kafka 会丢消息吗?
目录 01 生产者(Producer) 02 消息代理(Broker) 03 消费者(Consumer) 来源:Kafka 会丢消息吗? Kafka 会丢失信息吗? 许多开发人员普遍认为,Kafka 的设计本身就能保证不会丢失消息。然而,Kafka 架构和配置的细微差别会导致消息的丢失。我们需要了解它如何以及何时…...
状态模式详解与应用
状态模式(State Pattern),是一种行为型设计模式。它允许一个对象在其内部状态改变时改变它的行为,使得对象看起来似乎修改了它的类。通过将不同的行为封装在不同的状态类中,状态模式可以避免大量的条件判断语句&#x…...
红队工具使用全解析:揭开网络安全神秘面纱一角
红队工具使用全解析:揭开网络安全神秘面纱一角 B站红队公益课:https://space.bilibili.com/350329294 学习网盘资源链接:https://pan.quark.cn/s/4079487939e8 嘿,各位网络安全爱好者们!在风云变幻的网络安全战场上&am…...