当前位置: 首页 > news >正文

C# 与 C/C++ 的交互

什么是平台调用 (P/Invoke)

  • P/Invoke 是可用于从托管代码访问非托管库中的结构、回调和函数的一种技术。

托管代码与非托管的区别

    托管代码和非托管代码的主要区别是内存管理方式和对计算机资源的访问方式。托管代码通常运行在托管环境中,如 mono 或 java 虚拟机等,这些环境提供了垃圾回收器(GC)等工具来管理内存。在托管环境中,程序员通常不需要手动分配和释放内存,因为这些任务由运行时系统自动完成。托管代码通常具有更高的安全性和可移植性,因为它们运行在虚拟机中,而不是直接在操作系统上运行。相比之下,非托管代码通常是直接在操作系统上运行的,这意味着程序员需要手动分配和释放内存来避免内存泄漏和其他内存相关的问题。非托管代码可以更直接地访问计算机硬件和操作系统资源,因此它们通常具有更高的性能和更好的控制性。
  • P/Invoke API 包含在以下两个命名空间中:SystemSystem.Runtime.InteropServices。 使用这两个命名空间可提供用于如何与 native 通信的工具。

  • P/Invoke 从功能上来说,只支持函数调用,在被导出的函数前面一定要添加extern "C"来指明导出函数的时候使用C语言方式编译和连接,这样保证函数定义的名字和导出的名字相同,否则如果默认按C++方式导出,那个函数的名字就会变得乱七八糟,我们的程序就无法找到入口点了。

互调的基本原理

首先,我们需要了解 数据类型 的概念
在大多数编译型语言中定义变量的时候都需要先指定其数据类型,所谓数据类型,是语言约定的一个便于记忆的名称(int,long,float),究其本质就是在指明了这个数据在内存中到底是占用了几个字节,程序在运行的时候,首先找到这个数据的地址,然后再按照该类型的长度,读取响应的内存,然后再处理。
基于这个原理,编程语言直接就可以进行互调了,对于不同语言之间的互调,只要将改数据的指针(内存地址)传递给另一个语言,在另一个语言中根据通信协议将指针所指向的数据存储入长度对应的数据类型即可,当然要满足以下条件:

  1. 对于像 java,C# 这样有运行时虚拟机变成语言来讲,由于虚拟内存会让堆内存来回转移,因此在进行互调的时候,需要保证被调用的数据所在内存一定要固定,不能被转移。
  2. 有一些编程语言支持指针,有一些语言不支持指针(如Java),这个问题并不重要,所谓指针,其实就是一个内存地址,对于32位OS的指针是一个32位整数,而对于64位机OS的指针是一个64位整数。因为大多数语言中都有整型数,所以可以利用整型来接收指针。

Native 库的加载

举例被加载库的名称为: nativelib.so/nativelib.dll

  • 在 windows 平台运行时,将按以下顺序搜寻 dll:
    1. nativelib
    2. nativelib.dll
[DllImport("nativelib")]
[DllImport("nativelib.dll")]
static extern int ExportedFunction();
  • 在 linux 或 macOS 上运行时,运行时会尝试在 lib 前添加,并追加规范共享库扩展。在这些 OS 上按以下顺序尝试名称变体:
    1. nativelib.so/nativelib.dylib
    2. libnativelib.so/libnativelib.dylib
    3. nativelib
    4. libnativelib

基本数据类型传递

C# 关键字.Net类型C/C++
byteSystem.Byteuint8_t
sbyteSystem.SByteint8_t
shortSystem.Int16int16_t
ushortSystem.UInt16uint16_t
intSystem.Int32int32_t
uintSystem.UInt32uint32_t
longSystem.Int64int64_t
ulongSystem.UInt64uint64_t
charSystem.Charchar
nintSystem.IntPtrintptr_t
boolSystem.BooleanWin32 BOOL 类型
decimalSystem.DecimalCOM DECIMAL 结构

调用流程

基础数据类型调用

#define EXPORTAPI __declspec(dllexport)extern "C" EXPORTAPI int add(int a, int b)
{return a + b;
}
extern "C" EXPORTAPI void writeString(char* content)
{cout << content << endl;
}
  1. 第一行代码中定义了一个 EXPORTAPI 的宏,对应的内容是 __declspec(dllexport) 意思是将后面修饰的内容定义为 Dll 中要导出的内容 ,当然也剋以不使用这个宏,直接将 __declspec(dllexport) 写在要导出的函数前。
  2. 第二行中的 extern "C" ,表示该函数编译和连接时使用 C 语言的方式以保证函数名称不变,由于 C++ 支持函数重载,因此编译器会将函数的参数类型也加到编译后的代码中。例如函数 void fun(int,int),编译后的可能是 _fun_int_int(不同的编译器可能不同,但都采取了类似的机制),extern 是 C/C++ 语言中表明函数和全局变量的作用范围的关键字,这个关键字告诉编译器,其声明的函数和变量可以在本模块或其他模块中使用。
    [DllImport("TestCPPDll.dll",EntryPoint = "add")]extern static int add(int a, int b);[DllImport("TestCPPDll.dll")]extern static void writeString(string content);public static void Add(){int result = add(1, 2);Console.WriteLine($"CallLibraryAdd result :{result}");}public static void WriteString(string str){writeString(str);}

DllImport 中第一个参数表示 Dll 文件的位置,第二个参数 EntryPoint 用来指明对应的 C/C++ 中的函数名称是什么, extern 关键字表明该方法是一个外部调用,这个方法声明后,就可以像调用一个普通的静态方法一样去使用了。

指针的传递

extern "C" EXPORTAPI void addInt(int* i)
{*i += 1;cout << *i << endl;
}//传入一个整型数组的指针以及数组长度,遍历每一个元素并且输出
extern "C" EXPORTAPI void sendIntArray(int* firstElement, int arraylength)
{cout << "C# send int array to CPP" << endl;int* currentPointer = firstElement;for (int i = 0; i < arraylength; i++){cout << currentPointer[i];}cout << endl;
}

在第一个方法中参数为一个 int 类型的指针,并将其所指向的内容 +1

第二个方法传入一个整型数组的指针以及数组长度,遍历数组的每一个元素并且输出

    #region 指针的传递[DllImport("TestCPPDll.dll")]extern unsafe static void addInt(int* i);[DllImport("TestCPPDll.dll")]extern unsafe static void sendIntArray(int* firstElement, int arraylength);[DllImport("TestCPPDll.dll", EntryPoint = "getArrayFromCPP")]extern unsafe static IntPtr getArrayFromCPPUseInpPtr(IntPtr count);[DllImport("TestCPPDll.dll", EntryPoint = "getArrayFromCPP")]extern unsafe static IntPtr getArrayFromCPPUseRef(ref int count);// 调用 C++ 中的 AddInt 方法public static void AddInt(){int i = 10;unsafe{addInt(&i);}}//调用 C++ 中的 sendIntArray 方法将 C# 中的数组数据传递到 C++ 中,并在 C++ 中输出public static void AddIntArry(){int[] csArry = new int[10];for (int iArr = 0; iArr < 10; iArr++){csArry[iArr] = iArr;}unsafe{fixed (int* pCSArray = &csArry[0]){sendIntArray(pCSArray, 10);}}}//调用C++中的GetArrayFromCPP方法获取一个C++中建立的数组,使用 InpPrt 类型传参,IntPtr 与指针可以互相强转public static void GetArrayFromCPPUseInpPtr(){Console.WriteLine("CPP send int array to C# user IntPtr");int count = 0;GCHandle gch = GCHandle.Alloc(count, GCHandleType.Pinned);IntPtr countInptr = gch.AddrOfPinnedObject();IntPtr pArrayPointer = getArrayFromCPPUseInpPtr(countInptr);count = Marshal.PtrToStructure<int>(countInptr);int[] array = new int[count];for (int i = 0; i < count; i++){IntPtr intPtr = IntPtr.Add(pArrayPointer, i * Marshal.SizeOf<int>());array[i] = Marshal.PtrToStructure<int>(intPtr);Console.Write(array[i]);}Console.WriteLine();}//调用C++中的GetArrayFromCPP方法获取一个C++中建立的数组, 使用 ref 会出现防御性拷贝,造成 GCpublic static void GetArrayFromCPPUseRef(){Console.WriteLine("CPP send int array to C# user ref");int count = 0;IntPtr pArrayPointer = getArrayFromCPPUseRef(ref count);int[] array = new int[count];for (int i = 0; i < count; i++){IntPtr intPtr = IntPtr.Add(pArrayPointer, i * Marshal.SizeOf<int>());array[i] = Marshal.PtrToStructure<int>(intPtr);Console.Write(array[i]);}Console.WriteLine();}#endregion

函数指针的传递

C# 中并没有函数指针的概念,但是可以使用 委托 来代替函数指针

在 C/C++ 运行的某一时刻调用 C# 的对应函数, 这个时候就需要将一个 C# 中已经指向某一个函数的函数指针(委托)传递给 C++

//定义一个函数指针
typedef void (*OnCSCallBack)(int value);OnCSCallBack onCSCallBack;//用于设置函数指针的方法
extern "C" EXPORTAPI void setCallback(OnCSCallBack callback)
{onCSCallBack = callback;cout << "set CPP callback success" << endl;cout << endl;
}//对 C# 中传递过来的委托进行调用
extern "C" EXPORTAPI void testCPPInvoke() {int value = 999;onCSCallBack(value);
}
public delegate void OnCSDelegate(int value);[DllImport("TestCPPDll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]extern static void setCallback(OnCSDelegate action);[DllImport("TestCPPDll.dll")]extern static void testCPPInvoke();//测试过程中发现 .Net 中 deletegate 可以传递。action<int> 不行。//而在 Unity 中 将 Action 方法添加[MonoPInvokeCallback(typeof(Action<int>))] 标签后可以. MonoPInvokeCallback 标签在 AOT 命名空间下public static void SetCPPCallBack(){OnCSDelegate onCSCallBack = OnCSCallback;setCallback(onCSCallBack);}public static void OnCSCallback(int value){Console.WriteLine($"CPP call CSharp founction value:{value}");}

结构体的传递

传递结构体的想法和传递一个int类型数据类似,struct中的数据是在内存中顺序排列的

  • C#中结构体字段类型与C++结构体中的字段类型相兼容
  • C#结构中的字段顺序与C++结构体中的字段顺序相同,要保证该功能,需要将C#结构体标记为[StructLayout( LayoutKind.Sequential)]
typedef struct Vector3_
{float x;float y;float z;
} Vector3;// 输出从 C# 侧传入的结构体内容
extern "C" EXPORTAPI void sendStructFormCSToCPP(Vector3 vector3)
{cout << "get vector3 int cpp,x:";cout << vector3.x;cout << ",y:";cout << vector3.y;cout << ",z:";cout << vector3.z << endl;cout << endl;
}//包含固定长度数组的结构体
typedef struct ContainArray_
{Vector3 vectorArray[5];float valueArray[2];
}ContainArray;//不固定数组长度的结构体
typedef struct ContainArrayHasCount_
{int arrayCount;float* floatArray;int vectorArrayCount;Vector3* vectorArray;
}ContainArrayHasCount;//输出从 C# 侧传入数组固定长度的结构体信息
extern "C" EXPORTAPI void sendContainArrayStructFormCSToCPP(ContainArray containArray)
{int vectorArrayCount = sizeof(containArray.vectorArray) / sizeof(*containArray.vectorArray);for (size_t i = 0; i < vectorArrayCount; i++){cout << "vectorArray i: " << i << ",x:" << containArray.vectorArray[i].x << ",y:" << containArray.vectorArray[i].y << ",z:" << containArray.vectorArray[i].z << endl;}int valueArrayCount = sizeof(containArray.valueArray) / sizeof(*containArray.valueArray);for (size_t i = 0; i < valueArrayCount; i++){cout << "valueArray i: " + i << ", value: " << containArray.valueArray[i] << endl;}cout << endl;
}//输出运行时决定数组长度的结构体信息
extern "C" EXPORTAPI void sendContainArrayHasCountStructFormCSToCPP(ContainArrayHasCount containArrayHasCount)
{for (size_t i = 0; i < containArrayHasCount.arrayCount; i++){cout << "containArrayHasCount i: " << i << ",ArrayValue: " << containArrayHasCount.floatArray[i] << endl;}for (size_t i = 0; i < containArrayHasCount.vectorArrayCount; i++){cout << "vectorArray i: " << i << ",x:" << containArrayHasCount.vectorArray[i].x << ",y:" << containArrayHasCount.vectorArray[i].y << ",z:" << containArrayHasCount.vectorArray[i].z << endl;}
}//返回 C++ 侧创建的结构体指针
extern "C" EXPORTAPI ContainArrayHasCount * getArrayStructFormCPP()
{ContainArrayHasCount* containArrayHasCount = new ContainArrayHasCount();containArrayHasCount->arrayCount = 3;containArrayHasCount->floatArray = new float[3];for (size_t i = 0; i < containArrayHasCount->arrayCount; i++){containArrayHasCount->floatArray[i] = i;}containArrayHasCount->vectorArrayCount = 4;containArrayHasCount->vectorArray = new Vector3[4];for (size_t i = 0; i < containArrayHasCount->vectorArrayCount; i++){containArrayHasCount->vectorArray[i].x = i;containArrayHasCount->vectorArray[i].y = i;containArrayHasCount->vectorArray[i].z = i;}return containArrayHasCount;
}
    [StructLayout(LayoutKind.Sequential)]public struct Vector3{public Vector3(float x, float y, float z){X = x;Y = y;Z = z;}public float X, Y, Z;}[StructLayout(LayoutKind.Sequential)]public struct ContainArray{[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]public Vector3[] vectorArray;[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]public float[] valueArray;}[StructLayout(LayoutKind.Sequential)]public struct ContainArrayHasCount{public int floatArrayCount;public IntPtr floatArray;public int vectorArrayCount;public IntPtr vectorArray;}[DllImport("TestCPPDll")]extern static void sendStructFormCSToCPP(Vector3 vector3);[DllImport("TestCPPDll")]extern static void sendContainArrayStructFormCSToCPP(ContainArray containArrayStruct);[DllImport("TestCPPDll")]extern static void sendContainArrayHasCountStructFormCSToCPP(ContainArrayHasCount containArrayHasCount);[DllImport("TestCPPDll")]extern static IntPtr getArrayStructFormCPP();// 结构体作为参数传递给 C++ 方法public static void SendStructFormCSToCPP(){Vector3 vector3 = new Vector3();vector3.X = 1;vector3.Y = 2;vector3.Z = 3;sendStructFormCSToCPP(vector3);}// 传入包含指定长度数组的结构体public static void SendContainArrayStructFormCSToCPP(){ContainArray containArrayStruct = new ContainArray();containArrayStruct.vectorArray = new Vector3[5];for (int i = 0; i < 5; i++){containArrayStruct.vectorArray[i].X = i;containArrayStruct.vectorArray[i].Y = i;containArrayStruct.vectorArray[i].Z = i;}containArrayStruct.valueArray = new float[2];containArrayStruct.valueArray[0] = 1.234f;containArrayStruct.valueArray[1] = 5.678f;sendContainArrayStructFormCSToCPP(containArrayStruct);}// 传入运行时决定数组大小的结构体public static void SendContainArrayHasCountStructFormCSToCPP(){ContainArrayHasCount containArrayHasCount = new ContainArrayHasCount();float[] floatArray = new float[] { 1, 22, 333, 4444, 55555 };Vector3[] vectorArray = new Vector3[] { new Vector3(123, 123, 123), new Vector3(456, 456, 456), new Vector3(789, 789, 789) };containArrayHasCount.floatArrayCount = floatArray.Length;containArrayHasCount.vectorArrayCount = vectorArray.Length;GCHandle floatArrayHandle =  GCHandle.Alloc(floatArray,GCHandleType.Pinned);containArrayHasCount.floatArray = floatArrayHandle.AddrOfPinnedObject();GCHandle vectorArrayHandle = GCHandle.Alloc(vectorArray,GCHandleType.Pinned);containArrayHasCount.vectorArray = vectorArrayHandle.AddrOfPinnedObject();sendContainArrayHasCountStructFormCSToCPP(containArrayHasCount);floatArrayHandle.Free();vectorArrayHandle.Free();}// 接收从 C++ 返回的结构体public static void GetArrayStructFormCPP(){unsafe{IntPtr structIntPtr = getArrayStructFormCPP();ContainArrayHasCount containArrayHasCount = Marshal.PtrToStructure<ContainArrayHasCount>(structIntPtr);for (int i = 0; i < containArrayHasCount.floatArrayCount; i++){IntPtr floatIntPtr = IntPtr.Add(containArrayHasCount.floatArray,i*Marshal.SizeOf<float>());float value = Marshal.PtrToStructure<float>(floatIntPtr);Console.WriteLine($"floatArray {i}:{value}");}for (int i = 0; i < containArrayHasCount.vectorArrayCount; i++){IntPtr vectorIntPtr = IntPtr.Add(containArrayHasCount.vectorArray,i*Marshal.SizeOf<Vector3>());Vector3 vector = Marshal.PtrToStructure<Vector3>(vectorIntPtr);Console.WriteLine($"vectorArray {i}:{vector.X},{vector.Y},{vector.Z}");}Console.WriteLine($"floatArrayCount: {containArrayHasCount.floatArrayCount}");Console.WriteLine($"vectorArrayCount: {containArrayHasCount.vectorArrayCount}");}}

相关文章:

C# 与 C/C++ 的交互

什么是平台调用 (P/Invoke) P/Invoke 是可用于从托管代码访问非托管库中的结构、回调和函数的一种技术。 托管代码与非托管的区别 托管代码和非托管代码的主要区别是内存管理方式和对计算机资源的访问方式。托管代码通常运行在托管环境中&#xff0c;如 mono 或 java 虚拟机等…...

新版Android Studio搜索不到Lombok以及无法安装Lombok插件的问题

前言 在最近新版本的Android Studio中&#xff0c;使用插件时&#xff0c;在插件市场无法找到Lombox Plugin&#xff0c;具体表现如下图所示&#xff1a; 1、操作步骤&#xff1a; &#xff08;1&#xff09;打开Android Studio->Settings->Plugins&#xff0c;搜索Lom…...

BST二叉搜索树

文章目录 概述实现创建节点查找节点增加节点查找后驱值根据关键词删除找到树中所有小于key的节点的value 概述 二叉搜索树&#xff0c;它具有以下的特性&#xff0c;树节点具有一个key属性&#xff0c;不同节点之间key是不能重复的&#xff0c;对于任意一个节点&#xff0c;它…...

【Leetcode】211. 添加与搜索单词 - 数据结构设计

一、题目 1、题目描述 请你设计一个数据结构&#xff0c;支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。 实现词典类 WordDictionary &#xff1a; WordDictionary() 初始化词典对象void addWord(word) 将 word 添加到数据结构中&#xff0c;之后可以对它…...

Discuz户外旅游|旅行游记模板/Discuz!旅行社、旅游行业门户网站模板

价值328的discuz户外旅游|旅行游记模板&#xff0c;本模板需要配套【仁天际-PC模板管理】插件使用。 模板说明 1、模板页面宽度1200px&#xff0c;简洁大气&#xff0c;较适合户外旅行、骑行、游记、摩旅、旅游、活动等类型的论坛、频道网站&#xff1b; 2、所优化的页面有&…...

【重拾C语言】十一、外部数据组织——文件

目录 前言 十一、外部数据组织——文件 11.1 重新考虑户籍管理问题——文件 11.2 文件概述 11.2.1 文件分类 11.2.2 文件指针、标记及文件操作 11.3 打开、关闭文件 11.4 I/O操作 11.4.1 字符读写 11.4.2 字符串读写 11.4.3 格式化读写 11.4.4 数据块读写 11.4.5 …...

dpdk/spdk/网络协议栈/存储/网关开发/网络安全/虚拟化/ 0vS/TRex/dpvs技术专家成长体系教程

课程围绕安全&#xff0c;网络&#xff0c;存储&#xff0c;云原生4个维度去讲解核心技术点。 6个专栏组成&#xff1a;dpdk网络专栏、存储技术专栏、安全与网关开发专栏、虚拟化与云原生专栏、测试工具专栏、性能测试专栏 一、dpdk网络 dpdk基础知识 多队列网卡&#xff0…...

树莓派玩转openwrt软路由:5.OpenWrt防火墙配置及SSH连接

1、SSH配置 打开System -> Administration&#xff0c;打开SSH Access将Interface配置成unspecified。 如果选中其他的接口表示仅在给定接口上侦听&#xff0c;如果未指定&#xff0c;则在所有接口上侦听。在未指定下&#xff0c;所有的接口均可通过SSH访问认证。 2、防火…...

Gin:获取本机IP,获取访问IP

获取本机IP func GetLocalIP() []string {var ipStr []stringnetInterfaces, err : net.Interfaces()if err ! nil {fmt.Println("net.Interfaces error:", err.Error())return ipStr}for i : 0; i < len(netInterfaces); i {if (netInterfaces[i].Flags & ne…...

缓存降级代码结构设计

缓存降级设计思想 接前文缺陷点 本地探针应该增加计数器&#xff0c;多次异常再设置&#xff0c;避免网络波动造成误判。耦合度过高&#xff0c;远端缓存和本地缓存应该平行关系被设计为上下游关系了。公用的远端缓存的操作方法应该私有化&#xff0c;避免集成方代码误操作&…...

一文深入理解高并发服务器性能优化

我们现在已经搞定了 C10K并发连接问题 &#xff0c;升级一下&#xff0c;如何支持千万级的并发连接&#xff1f;你可能说&#xff0c;这不可能。你说错了&#xff0c;现在的系统可以支持千万级的并发连接&#xff0c;只不过所使用的那些激进的技术&#xff0c;并不为人所熟悉。…...

pytorch中的归一化函数

在 PyTorch 的 nn 模块中&#xff0c;有一些常见的归一化函数&#xff0c;用于在深度学习模型中进行数据的标准化和归一化。以下是一些常见的归一化函数&#xff1a; nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d&#xff1a; 这些函数用于批量归一化 (Batch Normalization…...

【管理运筹学】第 10 章 | 排队论(1,排队论的基本概念)

文章目录 引言一、基本概念1.1 排队过程1.2 排队系统的组成和特征1.3 排队模型的分类1.4 系统指标1.5 系统状态 引言 开一点排队论的内容吧&#xff0c;方便做题。 排队论&#xff08;Queuing Theory&#xff09;也称随机服务系统理论&#xff0c;是为解决一系列排队问题&…...

【Express】服务端渲染(模板引擎 EJS)

EJS&#xff08;Embedded JavaScript&#xff09;是一款流行的模板引擎&#xff0c;可以用于在Express中创建动态的HTML页面。它允许在HTML模板中嵌入JavaScript代码&#xff0c;并且能够生成基于数据的动态内容。 下面是一个详细的讲解和示例&#xff0c;演示如何在Express中…...

Linux CentOS8安装gitlab_ce步骤

1 下载安装包 wget --content-disposition https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/8/gitlab-ce-15.0.2-ce.0.el8.x86_64.rpm/download.rpm2 安装gitlab yum install policycoreutils-python-utilsrpm -Uvh gitlab-ce-15.0.2-ce.0.el8.x86_64.rpm3 更新配…...

RabbitMq启用TLS

Windows环境 查看配置文件的位置 选择使用的节点 查看当前节点配置文件的配置 配置TLS 将证书放到同配置相同目录中 编辑配置文件添加TLS相关配置 [{ssl, [{versions, [tlsv1.2]}]},{rabbit, [{ssl_listeners, [5671]},{ssl_options, [{cacertfile,"C:/Users/17126…...

CakePHP 3.x/4.x反序列化RCE链

最近网上公开了cakephp一些反序列化链的细节&#xff0c;但是没有公开poc&#xff0c;并且网上关于cakephp的反序列化链比较少&#xff0c;于是自己跟一下 &#xff0c;构造pop链。 CakePHP简介 CakePHP是一个运用了诸如ActiveRecord、Association Data Mapping、Front Contr…...

练习之C++[3]

文章目录 1.模板类2.模板声明3.string类 1.模板类 模板可以具有非类型参数&#xff0c;用于指定大小&#xff0c;可以根据指定的大小创建动态结构所以可用来创建动态增长和减小的数据结构模板运行时不检查数据类型&#xff0c;也不保证类型安全&#xff0c;相当于类型的宏替换…...

[MT8766][Android12] 修改WIFI热点默认名称、密码、IP地址以及默认开启热点

文章目录 开发平台基本信息问题描述解决方法 开发平台基本信息 芯片: MTK8766 版本: Android 12 kernel: msm-4.19 问题描述 最近做了一款没有屏幕显示的智能盒子&#xff0c;要想操控这款设备就只能通过adb投屏&#xff0c;如果默认不允许有线连接&#xff0c;那么要怎么实…...

【嵌入式】堆栈与单片机内存

堆栈 在片内RAM中&#xff0c;常常要指定一个专门的区域来存放某些特别的数据 它遵循顺序存取和后进先出(LIFO/FILO)的原则&#xff0c;这个RAM区叫堆栈。 其实堆栈就是单片机中的一些存储单元&#xff0c;这些存储单元被指定保存一些特殊信息&#xff0c;比如地址&#xff0…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

scikit-learn机器学习

# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...