C++ 与 Lua 联合编程

在软件开发的广阔天地里,不同编程语言各有所长。C++ 以其卓越的性能、强大的功能和对硬件的直接操控能力,在系统开发、游戏引擎、服务器等底层领域占据重要地位,但c++编写的程序需要编译,这往往是一个耗时操作,特别对于大型程序而言编译可能耗费几十分钟;而Lua 则凭借其轻量级、可嵌入性和灵活的脚本特性,在游戏脚本、配置管理等方面大放异彩。当 C++ 与 Lua 携手合作,它们能够优势互补,创造出更强大、更灵活的应用程序。本文将带你逐步深入了解 C++ 和 Lua 联合编程,从基础概念到实际应用,领略这种编程方式的魅力。

一、 C++ 与 Lua 联合编程

(一)C++ 与 Lua 的特点

C++ 是一种静态类型的编程语言,它拥有丰富的特性,如面向对象编程、泛型编程等。这使得 C++ 在处理大规模、高性能的系统级任务时表现出色,像游戏引擎、操作系统组件等对性能和资源管理要求极高的场景,C++ 都是不二之选。

Lua 则是一种轻量级的脚本语言,它的语法简洁,易于学习和使用。Lua 的核心优势在于其可嵌入性,能够轻松地被集成到其他应用程序中,为程序提供灵活的脚本功能。在游戏开发中,Lua 常被用于编写游戏逻辑脚本、用户界面交互脚本,以及实现游戏的热更新功能,让开发者无需重新编译整个程序,就能修改游戏内容。

(二)联合编程

将 C++ 和 Lua 联合编程,就像是让两位高手相互配合。C++ 负责搭建坚实的底层框架,处理计算密集型任务和资源管理;Lua 则专注于实现灵活多变的业务逻辑和用户交互。这种合作方式不仅能提高开发效率,还能使应用程序更具扩展性和可维护性。比如在游戏开发中,C++ 实现游戏的核心引擎功能,包括图形渲染、物理模拟等;Lua 则用于编写游戏角色的行为逻辑、任务脚本和关卡配置,这样当需要修改游戏内容时,只需要更新 Lua 脚本,无需重新编译 C++ 代码,大大缩短了开发周期。

二、搭建联合编程环境

(一)安装 Lua

  1. Windows 系统:从 Lua 官方网站下载 Windows 安装包,安装过程中记得勾选将 Lua 添加到系统环境变量。安装完成后,打开命令提示符,输入 “lua -v”,如果显示 Lua 的版本信息,就说明安装成功了。

  2. Linux 系统:以 Ubuntu 为例,在终端中执行 “sudo apt-get install lua5.3” 命令,就能轻松完成安装。安装后,同样可以在终端输入 “lua -v” 来验证是否安装成功。或者通过官网安装,整个安装过程很简单,就算遇到问题网上也能找到大量解决方法。

关于lua的安装和基础可以参考:Lua 从基础入门到精通(非常详细),本文着重于联合编程,lua和c++基础不会展开。

(二)准备 C++ 开发环境

  1. Windows 系统:推荐使用Visual Studio,官网点开下载,配置选择c++桌面应用

  2. Linux 系统:自带

三、C++ 与 Lua 的基础交互

(一)第一个程序:hello lua

1.首先引入头文件,在代码开头,使用 extern "C" 是因为 C++ 支持函数重载,会对函数名进行修饰,而 Lua 是用 C 语言编写的,其函数名没有经过修饰。通过 extern "C" 可以确保 C++ 编译器以 C 语言的方式处理这些 Lua 头文件中的函数名,避免链接错误。lua.h 提供了 Lua 核心功能的接口,lauxlib.h 是 Lua 辅助库,提供了一些方便的函数,lualib.h 则用于打开 Lua 标准库。

extern "C"
{#include <lua.h>#include <lauxlib.h>#include <lualib.h>
}

2.main 函数是程序的入口点。lua_open() 函数用于创建一个新的 Lua 状态机,它就像是一个容器,管理着 Lua 解释器的所有状态信息。随后,luaopen_base(L)luaopen_string(L) 和 luaopen_table(L) 分别打开了 Lua 的基础库、字符串库和表库。这些库提供了 Lua 编程中常用的功能,比如基础的算术运算、字符串处理和表操作等。

int main(int argc, char* argv[])
{lua_State* L = lua_open(); luaopen_base(L);luaopen_string(L);luaopen_table(L);

3.luaL_loadfile(L, "main.lua") 尝试加载名为 main.lua 的 Lua 脚本文件。如果加载过程中出现错误,该函数会返回一个非零值。一旦发生错误,程序会打印 loadfile error: 提示信息,并通过 lua_tostring(L, -1) 从 Lua 栈中获取错误信息(这里的-1值读取lua栈顶,应为出现异常lua会把错误提示信息压入栈顶),将其打印出来,最后返回 -1 表示程序异常退出。 

    if(luaL_loadfile(L,"main.lua")){printf("loadfile error:\n");const char* error = lua_tostring(L,-1);printf("\t%s\n",error);return -1;}

4.lua_pcall(L, 0, 0, 0) 用于执行之前加载的 Lua 脚本。lua_pcall 是一个安全的调用函数,它会捕获 Lua 脚本执行过程中抛出的异常。第一个参数 L 是 Lua 状态机,第二个参数 0 表示传递给 Lua 脚本的参数数量,第三个参数 0 表示期望从 Lua 脚本获取的返回值数量,第四个参数 0 表示错误处理函数的索引。如果执行过程中出现错误,同样会把错误信息压入lua栈顶,通过 lua_tostring(L, -1) 从 Lua 栈中获取错误信息,最后返回 -1。

    if(lua_pcall(L,0,0,0)){printf("pcall error:\n");const char* error = lua_tostring(L,-1);printf("\t%s\n",error);return -1;}

5。最后,lua_close(L) 用于关闭 Lua 状态机,释放相关的资源。

    lua_close(L);return 0;
}

 完整cpp文件:

extern "C"
{#include <lua.h>#include <lauxlib.h>#include <lualib.h>
}int main(int argc, char* argv[])
{lua_State* L = lua_open(); luaopen_base(L);luaopen_string(L);luaopen_table(L);if(luaL_loadfile(L,"main.lua")){printf("loadfile error:\n");const char* error = lua_tostring(L,-1);printf("\t%s\n",error);return -1;}if(lua_pcall(L,0,0,0)){printf("pcall error:\n");const char* error = lua_tostring(L,-1);printf("\t%s\n",error);return -1;}lua_close(L);return 0;
}

lua脚本(main.lua): 

print("hello lua")

(二)lua调用c++实现的函数

1.基本函数调用

先来看完整代码

extern "C"
{#include "lua.h"#include <lualib.h>#include <lauxlib.h>
}#include <iostream>
#include <string.h>int Ctest(lua_State* L)
{std::cout << "Cpp:Ctest" << std::endl;std::cout << lua_gettop(L) << std::endl;size_t len;const char* name = lua_tolstring(L,1,&len);int age = lua_tonumber(L,2);bool is = lua_toboolean(L,3);std::cout << lua_gettop(L) << std::endl;std::cout << "name: " << name << " age: " << age << " is: " << is << std::endl; return 0;
}int main(int argc, char* argv[])
{lua_State* L = lua_open();luaopen_base(L);lua_register(L,"ctest",Ctest);std::cout << "1: " <<lua_gettop(L) << std::endl;if(luaL_loadfile(L,"lesson1.lua")){std::cout << "load file failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << "2: " << lua_gettop(L) << std::endl;if(lua_pcall(L,0,0,0)){std::cout << "pcall failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << lua_gettop(L) << std::endl;lua_close(L);return 0;
}

lua脚本(lesson1.lua): 

ctest("xiaoming" , 13, nil)

我们定义的Ctest函数接收3个参数,字符串、数字、bool类型,代码运行时程序会把参数依次压入栈,压入完毕后从栈低开始从下往上算,第一个是字符串(索引1),第二个是数字(索引2),第三个是bool数据(索引3),我们分别用lua_tolstring,lua_tonumber,lua_toboolean从栈中取出这些数据并转换数据类型为CPP支持类型,这里的return 0指的是本函数的返回值个数是0个,也就是没有返回值。

在完成函数定义后,我们需要将定义的函数注册给lua脚本,使用lua_register(L,"ctest",Ctest);其中第一个参数是lua状态机,第二个参数是lua脚本中函数名,这个函数名可以和cpp中定义的函数名不同,第三个是cpp中实现函数的函数指针。

完成上述操作后,就可以在lua脚本中调用ctest函数了。

2.array类型数据作为参数

还是先来看完整代码

extern "C"
{#include "lua.h"#include <lualib.h>#include <lauxlib.h>
}#include <iostream>
#include <string.h>int Ctestarr(lua_State* L)
{std::cout << "ctestarr" << std::endl;int len =luaL_getn(L,-1);std::cout << "len: " << len << std::endl;for(int i = 0; i < len ; i++){lua_pushnumber(L,i+1);lua_gettable(L,1);//pop index push table[index]size_t size;std::cout << lua_tolstring(L,-1,&size) << std::endl;lua_pop(L,1);}return 0;
}int main(int argc, char* argv[])
{lua_State* L = lua_open();luaopen_base(L);lua_register(L,"ctestarr",Ctestarr);std::cout << "1: " <<lua_gettop(L) << std::endl;if(luaL_loadfile(L,"lesson2.lua")){std::cout << "load file failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << "2: " << lua_gettop(L) << std::endl;if(lua_pcall(L,0,0,0)){std::cout << "pcall failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << lua_gettop(L) << std::endl;lua_close(L);return 0;
}

脚本(lesson2.lua):

arr = {"xiaoming", "xiaohong", "xiaogang"}
ctestarr(arr)

我们定义的Ctestarr函数接收1个array数据,代码运行时程序会把参数压入栈,通过luaL_getn(L,-1)来计算array的长度,他的第一个参数是lua状态机,第二个是array在栈中位置,-1代表栈顶。获取长度后用佛如循环遍历读取array元素,每次读取先使用lua_pushnumber(L,i+1);向栈中压入一个索引值(lua索引从1开始,而不是从0开始,所以这里是i+1),然后执行lua_gettable(L,1);这个函数第二个参数是array在栈中位置,因为代码运行时程序会把参数压入栈,所以array一直在栈底(前面读取长度时用-1是因为栈内只有array,它即在栈底也在栈顶,而现在由于压入了索引,array已经不是栈顶了),lua_gettable(L,1);执行时会先对将lua栈中索引出栈,然后压入索引对于元素值,所以在tostring读取后记得执行lua_pop(L,1)恢复栈空间(lua_pop()函数第二个参数是出栈个数,出栈只从栈顶出,不是位置)。

3.带有键值对的table类型数据作为参数
extern "C"
{#include "lua.h"#include <lualib.h>#include <lauxlib.h>
}#include <iostream>
#include <string.h>int CtestTable1(lua_State* L)
{std::cout << "ctesttable1" << std::endl;lua_pushnil(L);while(lua_next(L,1) != 0)//每次调用从先栈顶弹出一个值,然后push key, push value{std::cout << lua_tostring(L,-2) << ": " << lua_tostring(L,-1) << std::endl;lua_pop(L,1);}return 0;
}int CtestTable2(lua_State* L)
{std::cout << "ctesttable2" << std::endl;lua_getfield(L,1,"name");//会把value压入栈顶std::cout << lua_tostring(L,-1) << std::endl;lua_pop(L,1);lua_getfield(L,1,"age");//会把value压入栈顶std::cout << lua_tostring(L,-1) << std::endl;lua_pop(L,1);return 0;
}int main(int argc, char* argv[])
{lua_State* L = lua_open();luaopen_base(L);lua_register(L,"ctesttable1",CtestTable1);lua_register(L,"ctesttable2",CtestTable2);std::cout << "1: " <<lua_gettop(L) << std::endl;if(luaL_loadfile(L,"lesson3.lua")){std::cout << "load file failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << "2: " << lua_gettop(L) << std::endl;if(lua_pcall(L,0,0,0)){std::cout << "pcall failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << lua_gettop(L) << std::endl;return 0;
}
arr = {name="xiaoming",age = "18", id = "22300"}
ctesttable1(arr)
ctesttable2(arr)

两种方法可以读取,lua_next(L,1)函数第二个参数是table在栈中位置,它在执行时会先出战一个数据,然后入栈key,最后入栈value,也就是在第一次读取时我们要先手动压栈一个nil值。读取结束后再手动出栈一个值。

第二种方法是lua_getfield(L,1,"name");第二个参数是table在栈中位置,第三个参数是要读取的key值,执行结束会将输入key对于的value值压栈,读取结束后需要手动出栈以复原占空间。

4.带返回值的情况
extern "C"
{#include "lua.h"#include <lualib.h>#include <lauxlib.h>
}#include <iostream>
#include <string.h>int CtestRe(lua_State* L)
{lua_pushstring(L,"return value");lua_pushnumber(L,100);lua_newtable(L);lua_pushstring(L,"key");lua_pushstring(L,"value");lua_settable(L,-3);lua_pushstring(L,"key2");lua_pushnumber(L,123);lua_settable(L,-3);return 3;
}int main(int argc, char* argv[])
{lua_State* L = lua_open();luaopen_base(L);lua_register(L, "ctestre", CtestRe);std::cout << "1: " <<lua_gettop(L) << std::endl;if(luaL_loadfile(L,"lesson5.lua")){std::cout << "load file failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << "2: " << lua_gettop(L) << std::endl;if(lua_pcall(L,0,0,0)){std::cout << "pcall failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << lua_gettop(L) << std::endl;return 0;
}
a,b,c = ctestre() 
print(a)
print(b)
for k,v in pairs(c) doprint(k,v)
end

我们在函数中执行lua_pushstring(L,"return value");  lua_pushnumber(L,100);向栈中压入一个string数据,一个number数据,执行lua_newtable(L);向栈中压入一个空table,然后一次压栈key值和value值,再执行lua_settable(L,-3);将压入的key和value加入table,这里的-3是之前压入的空table的栈中位置,因为压入了key和value,所以从占地开始从上往下第三个空间才是table,执行lua_settable后会把栈顶的key和value两个空间出栈,此时table又回到栈顶。

5.c++向lua中设置全局变量和读取lua中定义的全局变量
extern "C"
{#include "lua.h"#include <lualib.h>#include <lauxlib.h>
}#include <iostream>
#include <string.h>int main(int argc, char* argv[])
{lua_State* L = lua_open();luaopen_base(L);luaopen_string(L);luaopen_table(L);lua_pushstring(L,"value");//由c++设置全局变量lua_setglobal(L,"key");lua_pop(L,1);lua_pushnumber(L,18);//由c++设置全局变量lua_setglobal(L,"age");lua_pop(L,1);lua_newtable(L);lua_pushstring(L,"name");lua_pushstring(L,"xiaoming");lua_settable(L, -3);// 会把key,value出栈 lua_pushstring(L,"age");lua_pushnumber(L,13);lua_settable(L, -3);//还是-3lua_setglobal(L,"person");lua_pop(L,1);if(luaL_loadfile(L,"lesson6.lua")){std::cout << "load file failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}if(lua_pcall(L,0,0,0)){std::cout << "pcall failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << "1: " <<lua_gettop(L) << std::endl;lua_getglobal(L,"width");int width = lua_tonumber(L,-1);std::cout << "1: " <<lua_gettop(L) << std::endl;lua_pop(L,1);std::cout << "1: " <<lua_gettop(L) << std::endl;std::cout << "width = " << width << std::endl;lua_getglobal(L,"table");std::cout << "2: " <<lua_gettop(L) << std::endl;lua_getfield(L,-1,"age");std::cout << "2: " <<lua_gettop(L) << std::endl;std::cout << "age = " << lua_tonumber(L,-1) << std::endl;lua_pop(L,2);std::cout << "2: " <<lua_gettop(L) << std::endl;std::cout << lua_gettop(L) << std::endl;return 0;
}
print(key)
print(age)
for k,v in pairs(person) doprint(k,v)
end
table = {name = "xiaohong" , age = "16"}
width = 100

这部分很简单,要注意的就是栈空间的管理,并且设置全部变量的位置应该再pcall之前,读取应该在pcall之后,要不然读不到。

(三)c++调用lua实现的函数

1.调用函数
extern "C"
{#include "lua.h"#include <lualib.h>#include <lauxlib.h>
}#include <iostream>
#include <string.h>int main(int argc, char* argv[])
{lua_State* L = lua_open();luaopen_base(L);luaopen_string(L);luaopen_table(L);if(luaL_loadfile(L,"lesson7.lua")){std::cout << "load file failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}if(lua_pcall(L,0,0,0)){std::cout << "pcall failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << lua_gettop(L) << std::endl;lua_getglobal(L,"event");lua_pushstring(L,"xiaoming ");std::cout << lua_gettop(L) << std::endl;if(lua_pcall(L,1,1,0) != 0){std::cout << "pcall failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;lua_pop(L,1);}else{std::cout << "pcall success: " << lua_tostring(L,-1) << std::endl;std::cout << lua_gettop(L) << std::endl;lua_pop(L,1);}std::cout << lua_gettop(L) << std::endl;return 0;
}
function event(a)print("event")print(a)return "lua_event"
end

lua_getglobal(L,"event");先将函数压栈,由于函数需要一个参数,lua_pushstring(L,"xiaoming ");把参数压栈,之后调用lua_pcall(L,1,1,0)执行,这个函数的第二个参数是"event"函数参数个数(1个),第二个是"event"函数返回值个数(1个)函数执行会把event和参数出栈,把"event"函数返回值入栈。

2.错误处理函数(lua_pcall第四个参数设置)
extern "C"
{#include "lua.h"#include <lualib.h>#include <lauxlib.h>
}#include <iostream>
#include <string.h>int main(int argc, char* argv[])
{lua_State* L = lua_open();luaopen_base(L);luaopen_string(L);luaopen_table(L);if(luaL_loadfile(L,"lesson8.lua")){std::cout << "load file failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}if(lua_pcall(L,0,0,0)){std::cout << "pcall failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout << error << std::endl;return -1;}std::cout << lua_gettop(L) << std::endl;lua_getglobal(L,"err");int err = lua_gettop(L);lua_getglobal(L,"even");//故意少写一个t,触发错误lua_pushstring(L,"xiaoming ");if(lua_pcall(L,1,1,err) != 0){std::cout << "pcall failed" << std::endl;const char* error = lua_tostring(L, -1);std::cout <<"CPP ERR: "<< error << std::endl;lua_pop(L,1);}else{std::cout << "pcall success: " << lua_tostring(L,-1) << std::endl;std::cout << lua_gettop(L) << std::endl;lua_pop(L,1);}std::cout << lua_gettop(L) << std::endl;return 0;
}
function err(e)print("LUA_ERR: "..e)return "lua_error"
endfunction event(a)print("event")print(a)return "lua_event"
end

lua_pacll第四个参数是错误处理函数在栈中位置,正常当不设置错误处理时当发生错误,撸啊会把错误提示入栈,当设置之后,lua会把错误提示作为参数出啊如错误处理函数,错误处理函数执行后再把其返回值(这里是“lua_err”)压入栈。

四、联合编程的挑战与应对策略

(一)内存管理

在 C++ 和 Lua 联合编程中,内存管理是一个重要问题。由于 C++ 需要手动管理内存,而 Lua 有自己的垃圾回收机制,在传递数据时需要特别小心。例如,当 C++ 创建一个对象并传递给 Lua 时,需要确保在 Lua 使用完该对象后,C++ 能够正确地释放内存。可以通过智能指针等方式来辅助内存管理,确保对象在不再使用时被正确释放。

(二)异常处理

C++ 和 Lua 的异常处理机制不同,在联合编程时需要统一处理异常,以确保程序的稳定性。可以在 C++ 中捕获 Lua 脚本执行过程中抛出的异常,并进行适当的处理。例如,使用 “lua_pcall” 函数代替 “lua_call” 函数,“lua_pcall” 函数可以捕获 Lua 函数执行过程中的异常,并将异常信息压入 Lua 栈,C++ 代码可以从栈中获取异常信息并进行处理。

(三)性能优化

虽然 C++ 性能较高,但在与 Lua 交互时,频繁的栈操作和数据传递可能会带来性能开销。为了优化性能,可以尽量减少不必要的栈操作,批量传递数据,而不是逐个传递。同时,对性能敏感的代码部分,可以使用 C++ 实现,而将逻辑相对简单、变化频繁的部分交给 Lua 处理。

C++ 与 Lua 联合编程为开发者提供了强大的工具,让我们能够充分发挥两种语言的优势。通过深入学习和实践,你可以利用这种编程方式开发出更具扩展性、灵活性和高性能的应用程序。无论是游戏开发、脚本化工具还是其他领域,C++ 与 Lua 的联合都能为你的项目带来新的活力。

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

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

相关文章

烤箱面包烘焙状态图详解:从UML设计到PlantUML实现

题目&#xff1a;假设你正着手设计一个烤箱。建立一个跟踪烤箱中面包状态的状态图。要包括必要的触发器事件、动作和监视条件。 一、状态图概述 状态图是UML&#xff08;统一建模语言&#xff09;中的一种行为图&#xff0c;它用于描述系统中对象的状态变化以及触发这些变化的…

三款实用工具推荐:配音软件+Windows暂停更新+音视频下载!

各位打工人请注意&#xff01;今天李师傅掏出的三件套&#xff0c;都是经过实战检验的效率放大器。先收藏再划走&#xff0c;说不定哪天就能救命&#xff01; 一.祈风TTS-配音大师 做短视频的朋友肯定深有体会——配个音比写脚本还费劲&#xff01;要么付费买声音&#xff0c…

物流无人机结构与载货设计分析!

一、物流无人机的结构与载货设计模块运行方式 1.结构设计特点 垂直起降与固定翼结合&#xff1a;针对复杂地形&#xff08;如山区、城市&#xff09;需求&#xff0c;采用垂直起降&#xff08;VTOL&#xff09;与固定翼结合的复合布局&#xff0c;例如“天马”H型无人机&am…

Decode rpc invocation failed: null -> DecodeableRpcInvocation

DecodeableRpcInvocation 异常情况解决方法 错误警告官方FAQ 异常情况 记录一下Dubbo调用异常 java.util.concurrent.ExecutionException: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer. start time: 2025-05-07 22:09:5…

Excel VBA 词频统计宏

在Excel中&#xff0c;我们经常需要分析文本数据&#xff0c;例如统计某个单词或短语在文档中出现的次数。虽然Excel本身提供了一些文本处理功能&#xff08;如COUNTIF&#xff09;&#xff0c;但对于复杂的词频统计&#xff0c;手动操作可能效率低下。这时&#xff0c;VBA宏可…

DRV8301 三相电机驱动芯片的硬件参数与应用设计

DRV8301 硬件参数分析 1. 电源与驱动能力 输入电压范围&#xff1a;PVDD1&#xff08;主电源&#xff09;6V~60V&#xff0c;PVDD2&#xff08;降压转换器电源&#xff09;3.5V~60V&#xff0c;支持宽电压应用场景。 驱动电流&#xff1a;1.7A 源极驱动电流&#xff08;Sourc…

QT Sqlite数据库-教程03 插入数据-下

【1】手动提交事务 #include <QtSql/QSqlDatabase> #include <QtSql/QSqlQuery> #include <QtSql/QSqlRecord>QSqlDatabase db; db.transaction(); for(int i0; i<100000; i){QSqlQuery cmd(QString("UPDATE %1 SET %2%3 WHERE id%4").arg(tab…

LeetCode 每日一题 2025/4/28-2025/5/4

记录了初步解题思路 以及本地实现代码&#xff1b;并不一定为最优 也希望大家能一起探讨 一起进步 目录 4/28 2302. 统计得分小于 K 的子数组数目4/29 2962. 统计最大元素出现至少 K 次的子数组4/30 1295. 统计位数为偶数的数字5/1 2071. 你可以安排的最多任务数目5/2 838. 推多…

三、Hadoop1.X及其组件的深度剖析

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月7日 专栏&#xff1a;Hadoop教程 一、Hadoop 1.X 概述 &#xff08;一&#xff09;概念 Hadoop 是 Apache 开发的分布式系统基础架构&#xff0c;用 Java 编写&#xff0c;为集群处理大型数据集提供编程模型&#xff0c;…

Java中字符转数字的原理解析 - 为什么char x - ‘0‘能得到对应数字

前言 在Java编程中&#xff0c;我们经常需要将字符形式的数字转换为实际的数值。有很多方法可以实现这一转换&#xff0c;比如使用Integer.parseInt()或Character.getNumericValue()等方法。但有一种简便且高效的方式是直接使用char - 0运算&#xff0c;本文将详细解析这种方法…

第5讲、Transformer 编码器(Encoder)处理过程详解

&#x1f50d; Transformer 编码器&#xff08;Encoder&#xff09;处理过程详解 Transformer Encoder 是一个由 N 层&#xff08;一般为 6 层&#xff09;堆叠而成的模块结构。每一层的本质是两个核心子模块&#xff1a; 多头自注意力&#xff08;Multi-Head Self-Attention…

SWiRL:数据合成、多步推理与工具使用

SWiRL&#xff1a;数据合成、多步推理与工具使用 在大语言模型&#xff08;LLMs&#xff09;蓬勃发展的今天&#xff0c;其在复杂推理和工具使用任务上却常遇瓶颈。本文提出的Step-Wise Reinforcement Learning&#xff08;SWiRL&#xff09;技术&#xff0c;为解决这些难题带…

【Windows 常用工具系列 22 -- vscode markdown preview 字体大小设置】

文章目录 解决办法 解决办法 打开设置&#xff08;快捷键 Ctrl , 。或者左下角图标齿轮 ⚙&#xff09;搜索设置选项 Markdown › Preview: Font Size控制 Markdown 预览中使用的字号(以像素为单位)。 推荐阅读 https://blog.csdn.net/yanglsbb/article/details/127306685

【风控】模型监控和异常处理

在风控模型的全生命周期中&#xff0c;模型监控与异常处理是保障模型持续、稳定、可靠运行的关键环节。本指南旨在提供系统化、可落地的监控指标、预警策略及异常处置流程&#xff0c;帮助团队快速定位、响应并修复线上模型问题&#xff0c;最大限度降低风险。 1.模型监控与预…

第4章 递推法

4.1 递推法概述 设计思想&#xff1a; 递推法&#xff08;Recurrence Method&#xff09;通过已知的初始条件和递推关系&#xff0c;逐步推导出问题的最终结果&#xff0c;常用于序列计算和分阶段问题求解。 示例&#xff1a;猴子和桃子问题 题目描述&#xff1a; 猴子每天吃…

可视化魔法指南

🎨 ECharts数据可视化魔法指南 🌟 ECharts:数据的艺术画笔 #mermaid-svg-ARwFHUrXBJ03Gpo9 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ARwFHUrXBJ03Gpo9 .error-icon{fill:#552222;}#mermaid-svg-ARwFHUr…

SpringBoot学生宿舍管理系统开发实现

概述 一款基于SpringBoot框架开发的学生宿舍管理系统完整项目&#xff0c;该系统包含管理员、学生、宿管员和维修员四大角色模块&#xff0c;功能完善&#xff0c;非常适合作为设计或二次开发的基础项目。 主要内容 5.1 管理员功能模块 管理员登录界面采用验证码验证机制&a…

同步 / 异步、阻塞 / 非阻塞

前言 同步异步&#xff0c;在计算机科学中是一个非常重要的概念。作为一位软件开发工程师&#xff0c;我们每天都在和同步和异步打交道。 同步 同步-阻塞&#xff0c;顾名思义&#xff0c;就是同步和阻塞。调用方法后&#xff0c;必须等到结果返回&#xff0c;才能继续执行别…

AOP封装进行批量的数据查询并填充

在我们日常的项目开发中&#xff0c;我们经常会遇到这样的问题。我们有一张用户表&#xff0c;用户表中有用户ID和用户名称。我们其他表中会记录我们当前操作人的ID&#xff0c;一般&#xff0c;我们会记录一个创建人ID和修改人ID。那么&#xff0c;这个时候问题来了&#xff0…

Java学习手册:数据库事务相关知识

一、事务的概念与特性 概念 &#xff1a;事务是数据库中一系列操作的集合&#xff0c;这些操作要么全部成功&#xff0c;要么全部失败&#xff0c;是一个不可分割的工作单位。例如&#xff0c;在银行转账系统中&#xff0c;从一个账户扣款和向另一个账户存款这两个操作必须作为…