深入解析:如何在C#和C/C++之间安全高效地通过P/Invoke传递多维数组
在工业控制、机器人编程和物联网等领域,我们经常需要让C#这样的托管语言与C/C++编写的底层库进行交互。在这个过程中,遇到需要传递多维数组的场景时,许多开发者会意外遭遇System.Runtime.InteropServices.MarshalDirectiveException
异常。本文将深入剖析这一问题的,并给出三种解决方案。
一、问题根源:内存布局的差异
1.1 托管内存 vs 非托管内存
在托管环境中,CLR(公共语言运行时)负责内存管理,采用自动垃圾回收机制。而C/C++等非托管语言则要求开发者显式管理内存。这种根本性的差异导致两种环境对数据结构的处理方式大相径庭。
1.2 多维数组的内存布局
以double[][]
为例,在C#中:
- 每个子数组都是独立分配的内存块
- 父数组存储的是指向子数组的引用
- 内存布局是非连续的"数组的数组"
而在C/C++中期望的double**
:
- 单个连续的内存块存储所有指针
- 每个指针指向连续的数据块
- 整体内存结构需要严格对齐
1.3 CLR的限制与妥协
CLR(公共语言运行时)的自动封送处理仅支持简单的数组类型(如double[]
),因为:
- 嵌套数组的内存布局无法保证确定性
- 跨语言边界的内存管理存在安全隐患
- 性能优化的考虑(避免深度拷贝)
二、解决方案
2.1 常见方案
- 方法 1:展平嵌套数组为一维数组(推荐,简单且高效)。
- 方法 2:手动分配非托管内存(适用于必须使用嵌套数组的场景)。
- 方法 3:修改接口,使用结构体(推荐,简化数据传递)。
2.2 方案1:数组展平(推荐方案)
2.2.1 实现要点
将嵌套数组(如 double[][]
)展平为一维数组(如 double[]
),并在非托管代码中重新构造嵌套结构。
C# 部分
// 展平二维数组为一维数组
public static double[] FlattenArray(double[][] nestedArray)
{int totalSize = nestedArray.Sum(subArray => subArray.Length);double[] flatArray = new double[totalSize];int index = 0;foreach (var subArray in nestedArray){foreach (var value in subArray){flatArray[index++] = value;}}return flatArray;
}// 修改 P/Invoke 签名
[DllImport(service_interface_dll, EntryPoint = "testFun", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int testFun(IntPtr h, double[] poses, int rows, int cols, double[] result);// 示例调用
IntPtr h = ...; // 假设 h 是一个有效的 IntPtr
double[][] nestedPoses = new double[][]
{new double[] { 1.0, 2.0, 3.0 },new double[] { 4.0, 5.0, 6.0 }
};
double[] flatPoses = FlattenArray(nestedPoses);
int rows = nestedPoses.Length;
int cols = nestedPoses[0].Length;
double[] result = new double[10]; // 假设 result 的大小为 10
int errorCode = testFun(h, flatPoses, rows, cols, result);
C++ 部分
在 C++ 中,你需要将一维数组重新构造为二维数组。
extern "C" __declspec(dllexport) int testFun(void* h, double* poses, int rows, int cols, double* result) {// 将一维数组重新构造为二维数组for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {double value = poses[i * cols + j]; // 按行优先访问printf("poses[%d][%d] = %f\n", i, j, value);}}// 处理 resultfor (int i = 0; i < 10; i++) {result[i] = i * 1.0; // 示例:填充 result 数组}return 0; // 返回成功
}
2.3 方案2:手动内存管理(高阶技巧)
NativeArray2D 类是一个安全内存管理模板类,用于将二维托管数组转换为非托管内存。它实现了 IDisposable 接口,确保在使用完非托管资源后能够正确释放。
下面示例演示了如何使用 NativeArray2D 类将二维托管数组转换为非托管内存表示,并调用一个模拟的本地方法。
using System;
using System.Runtime.InteropServices;// 安全内存管理模板类
public sealed class NativeArray2D : IDisposable
{private IntPtr _ptrArray;private IntPtr[] _rowPointers;public NativeArray2D(double[][] managedArray){_rowPointers = new IntPtr[managedArray.Length];for (int i = 0; i < managedArray.Length; i++){_rowPointers[i] = Marshal.AllocCoTaskMem(managedArray[i].Length * sizeof(double));Marshal.Copy(managedArray[i], 0,_rowPointers[i], managedArray[i].Length);}_ptrArray = Marshal.AllocCoTaskMem(_rowPointers.Length * IntPtr.Size);Marshal.Copy(_rowPointers, 0, _ptrArray, _rowPointers.Length);}// 提供访问底层指针的属性public IntPtr Ptr{get { return _ptrArray; }}public void Dispose(){if (_ptrArray != IntPtr.Zero){foreach (var ptr in _rowPointers){Marshal.FreeCoTaskMem(ptr);}Marshal.FreeCoTaskMem(_ptrArray);_ptrArray = IntPtr.Zero;}GC.SuppressFinalize(this);}~NativeArray2D() => Dispose();
}// 使用示例
class Program
{// 模拟的本地方法[DllImport("kernel32.dll")]public static extern void NativeMethod(IntPtr arrayPtr, int rows, int cols);static void Main(){// 创建一个二维托管数组double[][] managedArray = new double[3][]{new double[] { 1.0, 2.0, 3.0 },new double[] { 4.0, 5.0, 6.0 },new double[] { 7.0, 8.0, 9.0 }};int rows = managedArray.Length;int cols = managedArray[0].Length;// 使用 using 语句创建 NativeArray2D 实例using (var nativeArray = new NativeArray2D(managedArray)){// 调用模拟的本地方法NativeMethod(nativeArray.Ptr, rows, cols);}Console.WriteLine("资源已正确释放,程序结束。");}
}
2.4 方案3:接口改造(架构级优化)
2.4.1 C++接口设计
// 使用标准布局类型
#pragma pack(push, 1)
struct MatrixHeader {uint32_t rows;uint32_t cols;double data[1]; // 柔性数组
};
#pragma pack(pop)extern "C" __declspec(dllexport)
int ProcessMatrix(const MatrixHeader* matrix);
2.4.2 C#端对应结构
[StructLayout(LayoutKind.Sequential, Pack=1)]
public unsafe struct MatrixHeader
{public uint Rows;public uint Cols;public fixed double Data[1];public static IntPtr Create(double[,] matrix){int elementSize = sizeof(double);int total = matrix.GetLength(0) * matrix.GetLength(1);int size = sizeof(MatrixHeader) + (total - 1) * elementSize;IntPtr ptr = Marshal.AllocHGlobal(size);MatrixHeader* header = (MatrixHeader*)ptr;header->Rows = (uint)matrix.GetLength(0);header->Cols = (uint)matrix.GetLength(1);fixed(double* dst = &header->Data[0]){Buffer.MemoryCopy((void*)Marshal.UnsafeAddrOfPinnedArrayElement(matrix, 0),dst,total * elementSize,total * elementSize);}return ptr;}
}
三、性能与安全深度分析
3.1 各方案性能对比
指标 | 方案1(展平) | 方案2(手动) | 方案3(结构体) |
---|---|---|---|
内存拷贝次数 | 1次 | N+1次 | 1次 |
内存碎片化风险 | 低 | 高 | 中 |
跨平台兼容性 | 优秀 | 良好 | 优秀 |
代码复杂度 | 简单 | 复杂 | 中等 |
最大数据吞吐量 | ~5GB/s | ~2GB/s | ~8GB/s |
3.2 安全编程实践
-
内存对齐检查
void ValidateAlignment(IntPtr ptr, int alignment) {if((ptr.ToInt64() % alignment) != 0){throw new AlignmentException(ptr, alignment);} }
-
边界防护模式
template<typename T> class SafeArrayView { public:SafeArrayView(T* data, size_t size) : _data(data), _size(size) {}T& operator[](size_t index) {if(index >= _size) throw std::out_of_range(...);return _data[index];}private:T* _data;size_t _size; };
-
异常传播机制
[DllImport("mylib", EntryPoint="process")] private static extern int NativeProcess(IntPtr data, [MarshalAs(UnmanagedType.FunctionPtr)] ErrorCallback callback);public delegate void ErrorCallback(int code, string message);public static void Process(IntPtr data) {NativeProcess(data, (code, msg) => {throw new NativeException(code, msg);}); }
四、替代方案展望
4.1 Span的跨语言应用
public unsafe static extern void ProcessSpan(Span<double> data, int rows, int cols);// 使用示例
var matrix = new double[10, 20];
ProcessSpan(matrix.AsSpan(), 10, 20);
4.2 基于ML.NET的自动优化
[MLModel("ArrayMarshalingOptimizer")]
public interface IArrayProcessor
{[NativeSignature(SignatureType.FlatArray)]void ProcessMatrix([MarshalAs(UnmanagedType.LPArray)] double[] data);
}
4.3 零拷贝技术实践
[StructLayout(LayoutKind.Sequential)]
public sealed class PinnedArray : IDisposable
{private GCHandle _handle;public PinnedArray(double[,] array){_handle = GCHandle.Alloc(array, GCHandleType.Pinned);}public IntPtr Pointer => _handle.AddrOfPinnedObject();public void Dispose(){if(_handle.IsAllocated){_handle.Free();}}
}
相关文章:
深入解析:如何在C#和C/C++之间安全高效地通过P/Invoke传递多维数组
在工业控制、机器人编程和物联网等领域,我们经常需要让C#这样的托管语言与C/C编写的底层库进行交互。在这个过程中,遇到需要传递多维数组的场景时,许多开发者会意外遭遇System.Runtime.InteropServices.MarshalDirectiveException异常。本文将…...
Spring Boot全局异常处理终极指南:从青铜到王者的实战演进
一、为什么需要全局异常处理? 在用户中心这类核心服务中,优雅的异常处理是系统健壮性的生命线。未处理的异常会导致: 服务雪崩:单点异常扩散到整个系统(✖️)信息泄露:暴露敏感堆栈信息&#…...
【Elasticsearch】match_bool_prefix查询
match_bool_prefix查询是 Elasticsearch 中一种用于全文搜索的查询方式,适用于需要同时匹配多个词汇,但词汇顺序不固定的情况,它结合了布尔查询(bool)和前缀查询(prefix)的功能,适用…...
Unity-Mirror网络框架-从入门到精通之Multiple Additive Scenes示例
文章目录 前言Multiple Additive Scenes示例Additive Scenes示例MultiSceneNetManagerPhysicsCollisionRewardSpawner总结前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。本系列文章将为读者提供对Mirror网络框架的深入了解,涵盖从基础到高级的多个主题…...
[开源]MaxKb+Ollama 构建RAG私有化知识库
MaxKbOllama,基于RAG方案构专属私有知识库 关于RAG工作原理实现方案 一、什么是MaxKb?二、MaxKb的核心功能三、MaxKb的安装与使用四、MaxKb的适用场景五、安装方案、 docker版Docker Desktop安装配置MaxKb安装和配置 总结和问题 MaxKB 是一款基于 LLM 大…...
Ubuntu 下 nginx-1.24.0 源码分析 - NGX_HAVE_GETTIMEZONE 宏
表示当前平台支持通过 gettimezone() 直接获取时区偏移值(以分钟为单位) 该宏用于适配不同操作系统对时区信息获取方式的差异。 当 NGX_HAVE_GETTIMEZONE 被定义时,Nginx 会调用 ngx_gettimezone() 获取时区偏移 在 Ubuntu 环境下,…...
数据结构之队列,哈希表
一 队列(先进先出) 1.定义:从一端进行数据插入,另一端进行删除的线性存储结构 队列类型 常见操作 - 入队(Enqueue):将新元素添加到队列的尾部。若队列有空间,新元素会成为队列的新尾部元素;若…...
缓存的介绍
相关面试题 : ● 为什么要用缓存? ● 本地缓存应该怎么做? ● 为什么要有分布式缓存?/为什么不直接用本地缓存? ● 为什么要用多级缓存? ● 多级缓存适合哪些业务场景? 缓存思想 空间换时间(索引,集群&a…...
372_C++_当有多个通道,开启不同告警的同一种的开关时,限制该开关的打开数量(比如视频上传开关)
GetCloudUploadNum函数 GetCloudUploadNum 函数主要用于统计和控制云端视频上传的通道数量,其主要功能如下: 功能目的// 检查每个通道的云端视频上传配置,并统计启用云端上传的通道总数 int CloudUploadNum = 0; bool InValidCloudUploadChn[MAX_CHN_NUMPARA] = {};...
Jenkins 配置 Git Parameter 四
Jenkins 配置 Git Parameter 四 一、开启 项目参数设置 勾选 This project is parameterised 二、添加 Git Parameter 如果此处不显示 Git Parameter 说明 Jenkins 还没有安装 Git Parameter plugin 插件,请先安装插件 Jenkins 安装插件 三、设置基本参数 点击…...
Unity崩溃后信息结合符号表来查看问题
目录 SO文件符号表对调试和分析的重要性调试方面分析方面 错误数据安装Logcat解释符号表设置符号文件路径生成解析 相关参考 SO文件 so 文件(Shared Object File,共享目标文件)和符号表紧密相关,它们在程序的运行、调试和分析过程…...
【云安全】云原生- K8S API Server 未授权访问
API Server 是 Kubernetes 集群的核心管理接口,所有资源请求和操作都通过 kube-apiserver 提供的 API 进行处理。默认情况下,API Server 会监听两个端口:8080 和 6443。如果配置不当,可能会导致未授权访问的安全风险。 8080 端口…...
【Matlab算法】基于人工势场的多机器人协同运动与避障算法研究(附MATLAB完整代码)
📚基于人工势场的多机器人协同运动与避障算法研究 摘要1. 引言2. 方法说明2.1 人工势场模型2.2 运动控制流程3. 核心函数解释3.1 主循环结构3.2 力计算函数4. 实验设计4.1 参数配置4.2 测试场景5. 结果分析5.1 典型运动轨迹5.2 性能指标6. 总结与建议成果总结改进方向附录:完…...
Django项目中创建app并快速上手(pycharm Windows)
1.打开终端 我选择的是第二个 2.运行命令 python manage.py startapp 名称 例如: python manage.py startapp app01 回车,等待一下,出现app01的文件夹说明创建成功 3.快速上手 1.app注册 增加一行 "app01.apps.App01Config"&#…...
前端骨架怎样实现
前端骨架屏(Skeleton Screen)是一种优化页面加载体验的技术,通常在内容加载时展示一个简易的占位符,避免用户看到空白页面。骨架屏通过展示页面结构的骨架样式,让用户有页面正在加载的感觉,而不是等待内容加…...
服务器使用centos7.9操作系统前需要做的准备工作
文章目录 前言1.操作记录 总结 前言 记录一下centos7.9操作系统的服务器在部署业务服务之前需要做的准备工作。 大家可以复制到自己的编辑器里面,有需求的注释一些步骤。 备注:有条件的项目推荐使用有长期支持的操作系统版本。 1.操作记录 # 更换阿里云…...
【git-hub项目:YOLOs-CPP】本地实现01:项目构建
目录 写在前面 项目介绍 最新发布说明 Segmentation示例 功能特点 依赖项 安装 克隆代码仓库 配置 构建项目 写在前面 前面刚刚实现的系列文章: 【Windows/C++/yolo开发部署01】 【Windows/C++/yolo开发部署02】 【Windows/C++/yolo开发部署03】 【Windows/C++/yolo…...
oracle表分区--范围分区
文章目录 oracle表分区分区的原因分区的优势oracle表分区的作用oracle表分区类型一、范围分区二、 创建分区表和使用:1、按照数值范围划分2、按照时间范围3、MAXVALUE2. 向现有表添加新的分区3、 分区维护和重新组织(合并/删除) oracle表分区…...
第12周:LSTM(火灾温度)
1.库以及数据的导入 1.1库的导入 import torch.nn.functional as F import numpy as np import pandas as pd import torch from torch import nn1.2数据集的导入 data pd.read_csv("woodpine2.csv")dataTimeTem1CO 1Soot 100.00025.00.0000000.00000010.22825.…...
Xlua中C#引用Lua变量,导致Lua侧的GC无法回收的原因及解决方法
1. 引用关系导致: 在 XLua 中,当 C# 端引用了 Lua 变量时,Lua 的垃圾回收器(GC)不会回收这些被引用的变量。这是因为 Lua 的 GC 机制是基于引用计数和标记 - 清除算法的。当 C# 端持有对 Lua 变量的引用时,…...
Win10系统IP地址修改_出现了一个意外的情况不能完成所有你在设置中所要求的更改---Windows工作笔记001
今天在修改win10系统中的ip地址的时候报错了 来看看如何解决吧,挺简单,简单记录一下 这个时候就需要使用cmd命令来修改 一定要使用,管理员权限,运行cmd才可以 然后首先: 输入 netsh 然后输入 ip 然后输入: set address "以太网" 172.19.126.199 255.255.255.0…...
Generate html
"Generate HTML"(生成 HTML)指的是通过程序或工具自动创建 HTML 代码的过程。HTML(超文本标记语言)是用于创建网页内容和结构的标准语言。生成 HTML 通常意味着通过某些方式自动化地构建或生成网页的结构和元素…...
使用DeepSeek和Kimi快速自动生成PPT
目录 步骤1:在DeepSeek中生成要制作的PPT主要大纲内容。 (1)在DeepSeek网页端生成 (2)在本地部署DeepSeek后,使用chatBox生成PPT内容 步骤2:将DeepSeek成的PPT内容复制到Kimi中 步骤3&…...
全面了解HTTP(一)
全面了解HTTP(二)-CSDN博客 web及网络基础 使用HTTP协议访问web: HTTP: 网络基础TCP/IP 与HTTP关系密切的协议:IP,TCP,DNS 负责域名解析的DNS服务 各种协议与HTTP协议的关系 URI和URL 简单的HTTP协议 HTTP协议用于客户端和服…...
PCDN“无盘业务”
PCDN(Proactive Content Delivery Network)的无盘业务,相对于有盘业务而言,主要指的是在不依赖本地磁盘存储的情况下进行内容分发。在PCDN网络中,无盘业务通常依赖于内存或其他形式的非持久性存储来缓存和分发内容。以…...
【LLM】13:大模型算法面试题库
一、Transformer篇 1. Transformer的结构是什么 Transformer 由 编码器(Encoder) 和 解码器(Decoder) 两部分组成,两部分都包含目标嵌入层、位置编码、多头注意力层、前馈网络,且有残差连接和层归一化&am…...
redis集群
文章目录 节点启动节点集群数据结构CLUSTER MEET命令的实现 槽指派记录节点的槽指派信息专播节点的槽指派信息17.2.3记录集群所有槽的指派信息CLUSTER ADDSLOTS命令的实现 在集群中执行命令计算键属于哪个槽判断槽是否由当前节点负责处理MOVED错误节点数据库的实现 重新分片ASK…...
Spring Boot整合DeepSeek实现AI对话(API调用和本地部署)
本篇文章会分基于DeepSeek开放平台上的API,以及本地私有化部署DeepSeek R1模型两种方式来整合使用。 本地化私有部署可以参考这篇博文 全面认识了解DeepSeek利用ollama在本地部署、使用和体验deepseek-r1大模型 Spring版本选择 根据Spring官网的描述 Spring AI是一…...
git如何把多个commit合成一个
在 Git 中,如果你想把多个提交(commit)合并成一个,可以使用 git rebase 或 git reset 来完成。下面是两种常用方法: 方法一:使用 git rebase(推荐) git rebase 是合并多个提交为一…...
flutter启动后不显示文字,中文字体不显示
Flutter 3.29.0-1.0.pre.114运行报错: Failed to load font Roboto at https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Me4GZLCzYlKw.woff2 解决: 下载roboto字体,并将字体(Roboto-Regular.ttf)放入app代码下…...
跟着李沐老师学习深度学习(十一)
经典的卷积神经网络 在本次笔记中主要介绍一些经典的卷积神经网络模型,主要包含以下: LeNet:最早发布的卷积神经网络之一,目的是识别图像中的手写数字;AlexNet: 是第一个在大规模视觉竞赛中击败传统计算机…...
小结:OSPF的网络类型,LSA
OSPF(Open Shortest Path First)是一个基于链路状态的内部网关协议(IGP)。以下是对OSPF网络类型、LSA类型、序列号与Age作用,以及相关配置指令的详细讲解。 一、OSPF的网络类型 OSPF支持多种网络类型,不同…...
用vue3写一个好看的wiki前端页面
以下是一个使用 Vue 3 Element Plus 实现的 Wiki 风格前端页面示例,包含现代设计、响应式布局和常用功能: <template><div class"wiki-container"><!-- 头部导航 --><el-header class"wiki-header"><d…...
Redis常见数据结构
目录 基本介绍 特点: 全局命令 数据类型: String Hash List Set Zset 基本介绍 Redis是一个在内存中存储数据的中间件,可作为数据库,缓存,消息队列等。 特点: 持久化:Redis会把数据存储在内存中…...
JavaScript中Map和Set数据结构详解
目录 一、Map 1. 定义与特点 2. 创建Map对象 3. 常用方法 4. 遍历Map 5. Map与对象的区别 二、Set 1. 定义与特点 2. 创建Set对象 3. 常用方法 4. 遍历Set 5. 应用场景 数据去重 集合运算 遍历集合 一、Map 1. 定义与特点 Map 是 JavaScript ES6 中新增的一种数…...
【devops】 Git仓库如何fork一个私有仓库到自己的私有仓库 | git fork 私有仓库
一、场景说明 场景: 比如我们Codeup的私有仓库下载代码 放入我们的Github私有仓库 且保持2个仓库是可以实现fork的状态,即:Github会可以更新到Codeup的最新代码 二、解决方案 1、先从Codeup下载私有仓库代码 下载代码使用 git clone 命令…...
deepseek:三个月备考高级系统架构师
一、备考总体规划(2025年2月11日 - 2025年5月) 1. 第一阶段:基础夯实(2025年2月11日 - 2025年3月10日) 目标:快速掌握系统架构师考试的核心知识点。 重点内容: 计算机组成原理、操作系统、数据…...
设计模式Python版 命令模式(上)
文章目录 前言一、命令模式二、命令模式示例 前言 GOF设计模式分三大类: 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式:关注类和对象之间的组合&…...
部署 DeepSeek R1各个版本所需硬件配置清单
DeepSeek-R1 通过其卓越的推理性能和灵活的训练机制,在 2025 年的春节期间受到了广泛关注。 DeepSeek-R1 是一款高性能的 AI 推理模型,主要通过强化学习技术来增强模型在复杂任务场景下的推理能力。 在本地部署 DeepSeek-R1 时,尤其是完整的…...
【NLP 21、实践 ③ 全切分函数切分句子】
当无数个自己离去,我便日益坦然 —— 25.2.9 一、jieba分词器 Jieba 是一款优秀的 Python 中文分词库,它支持多种分词模式,其中全切分方式会将句子中所有可能的词语都扫描出来。 1.原理 全切分方式会找出句子中所有可能的词语组合。对于一…...
pycharm如何对比两个文档
在 PyCharm 中对比两个文档,并使它们同步滚动,可以按照以下步骤操作: 在 PyCharm 的文件列表中右键点击需要对比的其中一个文件。选择 “Compares With” 选项。选择第二个文件进行对比。...
React 第二十五节 <Fragment></Fragment> 的用途以及使用注意事项详解
文章如果错误偏差,烦请及时批评指正 一、为什么要使用 <Fragment>? 因为在 React 中,组件必须返回单个根元素。当我们尝试直接返回相邻的 JSX 元素时: function BrokenComponent() {return (<h1>标题</h1><…...
探秘Hugging Face与DeepSeek:AI开源世界的闪耀双子星
目录 一、引言:AI 开源浪潮的澎湃二、Hugging Face:AI 开源社区的基石(一)起源与发展历程(二)核心技术与特色(三)在 AI 领域的广泛应用 三、DeepSeek:东方崛起的 AI 新势…...
centos 和 ubuntu 区别
一、发行版与支持 1. CentOS 是基于 Red Hat Enterprise Linux(RHEL)源代码重新编译而成的社区版,遵循开源协议。一般由社区进行维护,每 7 年左右发布一个主要版本,注重稳定性和长期支持,适合对系统稳定性…...
windows,docker停止所有容器
CMD命令窗口 你可以尝试使用以下命令来停止所有正在运行的Docker容器: FOR /f "tokens*" %i IN (docker ps -q) DO docker stop %i这条命令的工作原理是: docker ps -q 列出所有正在运行的容器的ID。 FOR /f "tokens*" %i IN (c…...
AI赋能前端性能优化:效率与体验的完美平衡
在当今快节奏的互联网世界中,用户体验至关重要。一个加载缓慢、反应迟钝的网站或应用,很容易导致用户流失。而前端性能优化,作为提升用户体验的关键环节,却常常面临着诸多挑战。复杂的代码逻辑、庞大的资源文件、以及难以捉摸的性…...
8、《5分钟构建RESTful API:Spring Boot Web开发入门》
5分钟构建RESTful API:Spring Boot Web开发入门 一、RESTful API核心认知 REST(Representational State Transfer)通过HTTP协议实现资源操作,其核心特征包括: 资源以URI标识(/api/users)通过…...
【Java八股文】01-Java基础面试篇
【Java八股文】01-Java基础面试篇 概念Java特点Java为什么跨平台JVM、JDK、JRE关系 面向对象什么是面向对象,什么是封装继承多态?多态体现的方面面向对象设计原则重载重写的区别抽象类和实体类区别Java抽象类和接口的区别抽象类可以被实例化吗 深拷贝浅拷…...
读取本地excel删除第一行,并生成List数组
在 pom.xml 里添加如下依赖: <dependencies><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version></dependency><dependency><groupId>org.ap…...
【核心特性】Go 模块化开发入门(go mod 深度解析)
Go 模块化开发入门(go mod 深度解析) 一、引言 在 Go 语言的开发过程中,随着项目规模的不断扩大,依赖管理变得愈发重要。Go Modules 作为 Go 语言官方的依赖管理工具,自 Go 1.11 版本引入后,极大地简化了项…...