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
-
Windows 系统:从 Lua 官方网站下载 Windows 安装包,安装过程中记得勾选将 Lua 添加到系统环境变量。安装完成后,打开命令提示符,输入 “lua -v”,如果显示 Lua 的版本信息,就说明安装成功了。
-
Linux 系统:以 Ubuntu 为例,在终端中执行 “sudo apt-get install lua5.3” 命令,就能轻松完成安装。安装后,同样可以在终端输入 “lua -v” 来验证是否安装成功。或者通过官网安装,整个安装过程很简单,就算遇到问题网上也能找到大量解决方法。
关于lua的安装和基础可以参考:Lua 从基础入门到精通(非常详细),本文着重于联合编程,lua和c++基础不会展开。
(二)准备 C++ 开发环境
-
Windows 系统:推荐使用Visual Studio,官网点开下载,配置选择c++桌面应用
-
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 的联合都能为你的项目带来新的活力。
相关文章:
C++ 与 Lua 联合编程
在软件开发的广阔天地里,不同编程语言各有所长。C 以其卓越的性能、强大的功能和对硬件的直接操控能力,在系统开发、游戏引擎、服务器等底层领域占据重要地位,但c编写的程序需要编译,这往往是一个耗时操作,特别对于大型…...
瑞萨 EZ-CUBE2 调试器
瑞萨 EZ-CUBE2 调试器 本文介绍了瑞萨 EZ-CUBE2 调试器的基本信息、调试方式、环境搭建、硬件连接、软件测试等。 包装展示 调试器展示 开关选项 详见:EZ-CUBE2 | Renesas 瑞萨电子 . 环境搭建 使用 Renesas 公司的 e2 studio 开发工具,下载 并安装该…...
MATLAB滤波工具箱演示——自定义维度、滤波方法的例程演示与绘图、数据输出
使用 M A T L A B MATLAB MATLAB的界面做了一个 M A T L A B MATLAB MATLAB滤波工具箱 d e m o demo demo,本文章给出演示:自定义维度、滤波方法的例程演示与绘图、数据输出 文章目录 编辑界面使用方法优势待改进点部分代码 编辑界面 使用 M A T L A B …...
数据库索引优化实战: 如何设计高效的数据库索引
数据库索引优化实战: 如何设计高效的数据库索引 一、理解数据库索引的核心原理 1.1 B树索引的结构特性 数据库索引(Database Index)的本质是通过特定数据结构加速数据检索。现代关系型数据库普遍采用B树(B Tree)作为默认索引结构&…...
TS 安装
TS较JS优势 1 TS静态类型编程语言。编译时发现错误 2 类型系统 强化变量类型概念 3 支持新语法 4 类型推断机制 可以和React框架中的各种hook配合 5 任何地方都有代码提示 tsc 命令 将TS转为JS 1 tsc 文件.ts 生成 js文件 2 执行JS代码...
CMake separate_arguments用法详解
separate_arguments 是 CMake 中用于将字符串分割成参数列表的命令,适用于处理包含空格的参数或复杂命令行参数。以下是其用法详解: 基本语法 separate_arguments(<variable> [UNIX|WINDOWS_COMMAND] [PROGRAM <program>] [ARGS <args&…...
【AI科技】AMD ROCm 6.4 新功能:突破性推理、即插即用容器和模块化部署,可在 AMD Instinct GPU 上实现可扩展 AI
AMD ROCm 6.4 新功能:突破性推理、即插即用容器和模块化部署,可在 AMD Instinct GPU 上实现可扩展 AI 现代 AI 工作负载的规模和复杂性不断增长,而人们对性能和部署便捷性的期望也日益提升。对于在 AMD Instinct™ GPU 上构建 AI 和 HPC 未来…...
2025年- H20-Lc128-240. 搜索二维矩阵 II(矩阵)---java版
1.题目描述 2.思路 遍历矩阵,然后如果遇到矩阵中的值正好等于target,输出true。否则,输出false。 3.代码 public class H240 {public boolean searchMatrix(int[][] matrix, int target) {//1.计算出总的行值,总的列值。int mm…...
LearningFlow:大语言模型城市驾驶的自动化策略学习工作流程
《LearningFlow: Automated Policy Learning Workflow for Urban Driving with Large Language Models》2025年1月发表,来自香港科技大学广州分校的论文。 强化学习(RL)的最新进展表明了自动驾驶的巨大潜力。尽管有这一前景,但奖励…...
C语言数据类型与内存布局
C语言数据类型内存占用 类型32位系统64位系统格式说明符char1字节1字节%cint4字节4字节%dfloat4字节4字节%fdouble8字节8字节%lflong long8字节8字节%lld...
从原理到实战讲解回归算法!!!
哈喽,大家好,我是我不是小upper, 今天系统梳理了线性回归的核心知识,从模型的基本原理、参数估计方法,到模型评估指标与实际应用场景,帮助大家深入理解这一经典的机器学习算法,助力数据分析与预测工作。 …...
linux指令中的竖线(“|”)是干啥的?【含实例展示】
文章目录 一、管道符的基本概念二、管道符的核心作用三、常用实例展示四、进阶技巧五、注意事项总结 实操展示**案例1:统计日志中特定错误的数量****案例2:查找当前运行的进程****案例3:合并排序并去重****案例4:实时监控CPU占用前…...
[HOT 100] 0124. 二叉树中的最大路径和
文章目录 1. 题目链接2. 题目描述3. 题目示例4. 解题思路5. 题解代码6. 复杂度分析 1. 题目链接 124. 二叉树中的最大路径和 - 力扣(LeetCode) 2. 题目描述 二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一…...
[SoC]AXI总线Performance验证方案
AXI总线Performance验证方案 测试 AXI (Advanced eXtensible Interface) 的性能是 SoC 验证中的重要任务,旨在评估其在不同负载和配置下的表现是否满足设计要求。以下详细说明如何测试 AXI 的性能、需要统计的变量、计算方法、在验证环境中动态计算性能的方法,以及如何…...
EMC PowerStore存储学习之一NVMe磁盘的命名规则
PowerStore的日志中经常会看到类似于/dev/nvme1n1的磁盘,在svc_diag list --show_drives中也可以看到类似这样的输出,如下图: 这里的Drives的显示都是 /dev/nvmeXnY的形式,那么这个磁盘命名规则怎么解读呢? 在Linux系…...
apt-mirror搭建ubuntu本地离线源
参考资料 4 Steps to Setup Local Repository in Ubuntu using APT-mirror 使用 APT-mirror 四步配置 Ubuntu 本地软件仓库 ubuntu下的apt-get内网本地源的搭建...
【记录】新Ubuntu20配置voxelmap的环境安装
因为系统总出问题,仅用于个人纪录。 1. 升级CMake到3.28及以上版本(Sophus依赖) wget https://github.com/Kitware/CMake/releases/download/v3.28.3/cmake-3.28.3-linux-x86_64.sh chmod x cmake-3.28.3-linux-x86_64.sh sudo ./cmake-3.2…...
Python全流程开发实战:基于IMAP协议安全下载个人Gmail邮箱内所有PDF附件
在日常办公场景中,面对成百上千封携带PDF附件的邮件,手动逐一下载往往耗时耗力,成为效率瓶颈。如何通过代码实现“一键批量下载”?本文将以**“Gmail全量PDF附件下载工具”**开发为例,完整拆解从需求分析到落地交付的P…...
CPU:AMD的线程撕裂者(Threadripper)和霄龙(EPYC)的区别
AMD的**线程撕裂者(Threadripper)和霄龙(EPYC)**虽然都是面向高性能市场的处理器,但它们在定位、功能和技术规格上有显著区别。以下是两者的主要差异: 1. 目标市场 线程撕裂者(Threadripper&…...
【五一培训】Day 2
注: 1. 本次培训内容的记录将以“Topic”的方式来呈现,用于记录个人对知识点的理解。 2. 由于培训期间,作者受限于一些现实条件,本文的排版及图片等相关优化,需要过一段时间才能完成。 3. 关于老板点评的一些思考 你…...
shell_plus
python manage.py shell_plus 是由 django-extensions 提供的一个增强版的 Django shell,它自动导入你的所有模型和其他一些便捷功能,使得交互式开发更加方便。 如果你遇到配置或运行问题,特别是与 RQ_SHOW_ADMIN_LINK 相关的 ImproperlyCon…...
基于C++、JsonCpp、Muduo库实现的分布式RPC通信框架
⭐️个人主页:小羊 ⭐️所属专栏:RPC框架 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 项目介绍JsonCpp库简单介绍Muduo库简单介绍C11异步操作——std::future1. 使用 std::async 关联异步任务2. std::packaged_task…...
Redis TLS 加密对性能的影响分析
Redis TLS 加密对性能的影响分析 是的,Redis 启用 TLS 加密确实会对性能产生一定影响,但影响程度取决于多种因素。以下是详细分析: 一、性能影响的主要来源 加密/解密开销: TLS 握手过程中的非对称加密(如 RSA、…...
树与二叉树完全解析:从基础到应用
目录 一、树形结构的基础认知 1.1 树的定义与特点 1.2 核心术语解析 二、二叉树的深度解析 2.1 二叉树定义 2.2 特殊二叉树类型 2.3 重要性质总结 三、二叉树的存储与遍历 3.1 存储方式对比 3.2 遍历算法精讲 四、经典题型训练 4.1 相同树判断(LeetCode…...
PostgreSQL:pgJDBC 下载和安装
PostgreSQL 的 pgJDBC 是用于 Java 程序连接和操作 PostgreSQL 数据库的 JDBC 驱动程序。 PostgreSQL:pgJDBC v42.7 下载和安装 点击【Application Stack Builder】 安装目录: 运行 cmd cd D:\PostgreSQL\pgJDBC copy postgresql-42.7.2.jar D:\groovy-…...
正则表达式与文本三剑客grep、sed、awk
目录 一、正则表达式 1.1、字符匹配 1.2、次数匹配 1.3、位置锚定 1.4、分组或其他 二、扩展正则表达式 三、grep 四、awk 4.1、常用命令选项 4.2、工作原理 4.3、基础用法 4.4、内置变量 4.5、模式 4.6、条件判断 4.7、awk中的循环语句 4.8、数组 4.9、脚本 …...
(35)VTK C++开发示例 ---将图片映射到平面2
文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容👉内容导航 👈👉VTK开发 👈 1. 概述 与上一个示例不同的是,使用vtkImageReader2Factory根据文件扩展名或内容自动创建对应的图像文件读取器&a…...
每日一题洛谷P8635 [蓝桥杯 2016 省 AB] 四平方和c++
P8635 [蓝桥杯 2016 省 AB] 四平方和 - 洛谷 (luogu.com.cn) 直接暴力枚举,不做任何优化的话最后会TLE一个,稍微优化一下就过了(数据给的还是太良心了) 优化:每层循环用if判断一下,如果大于n就直接跳 当然…...
【python】【UV】一篇文章学完新一代 Python 环境与包管理器使用指南
🐍 UV:新一代 Python 环境与包管理器使用指南 一、UV 是什么? UV 是由 Astral 团队开发的高性能 Python 环境管理器,旨在统一替代 pyenv、pip、venv、pip-tools、pipenv 等工具。 1.1 UV 的主要功能 🚀 极速包安装&…...
6.10.单源最短路径问题-Dijkstra算法
一.BFS算法的局限性: 如上图,BFS算法可以解决无权图的单源最短路径问题, 如果是解决带权图的单源最短路径问题,BFS算法就不适用了,如下图: 如上图,比如求G港到其他顶点的最短路径, …...
Python基于深度学习的网络舆情分析系统(附源码,部署)
大家好,我是Python徐师兄,一个有着7年大厂经验的程序员,也是一名热衷于分享干货的技术爱好者。平时我在 CSDN、掘金、华为云、阿里云和 InfoQ 等平台分享我的心得体会。 🍅文末获取源码联系🍅 2025年最全的计算机软件毕…...
mysql--索引
索引作为一种数据结构,其用途是用于提升检索数据的效率。 分类 普通索引(INDEX):索引列值可重复 唯一索引(UNIQUE):索引列值必须唯一,可以为NULL 主键索引(PRIMARY KEY&a…...
【算法题】荷兰国旗问题[力扣75题颜色分类] - JAVA
一、题目 二、文字解释 1.1 前言 本题是经典的「荷兰国旗问题」,由计算机科学家 Edsger W. Dijkstra 首先提出。如同图中所示的荷兰国旗,其由红、白、蓝三色水平排列组成。在算法领域,该问题可类比为将一个由特定的三种元素(可…...
【数据结构】堆的完整实现
堆的完整实现 堆的完整实现GitHub地址前言堆的核心功能实现重温堆的定义堆结构定义1. 堆初始化与销毁2. 元素交换函数3. 堆化操作向上调整(子→父)向下调整(父→子) 4. 堆元素插入5. 堆元素删除6. 辅助功能函数堆的判空获取堆顶元…...
软考 系统架构设计师系列知识点之杂项集萃(51)
接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(50) 第80题 设三个煤场A1、A2、A3分别能供应煤7、12、11万吨,三个工厂B1、B2、B3分别需要10、10、10万吨,从各煤场到各工厂运煤的单价(百元/吨&…...
patch命令在代码管理中的应用
patch 是一个用于将差异文件(补丁)应用到源代码的工具,常用于修复 bug、添加功能或调整代码结构。在您提供的代码中,patch 命令通过一系列补丁文件(.patch)修改了 open-amp 库的源代码。 patch 命令的核心作…...
Qt结构体运算符重载指南
在 Qt 中,结构体(struct)或类(class)中重载运算符是一种常见的做法,用于实现自定义类型的逻辑操作(如比较、算术运算等)。以下是一些常见的运算符重载示例和注意事项: 1.…...
基于bert预训练模型的垃圾短信分类系统
文章目录 任务介绍数据说明注意事项数据处理数据准备数据集划分数据集类构建模型构建与训练模型构建模型训练模型推理附录任务介绍 随着移动通信技术的飞速发展,短信(Short Message Service, SMS)已成为人们日常生活中不可或缺的沟通方式之一。然而,垃圾短信(Spam SMS)的…...
[Android] 网易爆米花TV 2.0.0.0429(原网易Filmly,支持多网盘的TV版、电脑版带海报墙播放器)
[Android] 网易爆米花 链接:https://pan.xunlei.com/s/VOPDuQS9D7qixvAnoy7-he2PA1?pwdhzvh# [Android] 网易爆米花TV 2.0.0.0429(原网易Filmly,支持多网盘的TV版、电脑版带海报墙播放器) 详细介绍直接上主页截图,…...
# 前后端分离象棋对战项目开发记录
1. **结构清晰**:使用更直观的标题、分段和列表,增强可读性。 2. **视觉美观**:添加Markdown格式化(如代码块、加粗、斜体),并建议配色和排版风格。 3. **内容精炼**:精简冗余表述,突…...
Android Framework学习二:Activity创建及View绘制流程
文章目录 Window绘制流程Window Manager Service(WMS)SurfaceSurfaceFlinger 安卓View层次结构ActivityPhoneWindowActivity与PhoneWindow两者之间的关系ViewRootImplDecorViewDecorView 的作用DecorView 的结构总结 Activity创建流程View invalidate调用…...
文章五《卷积神经网络(CNN)与图像处理》
文章5:卷积神经网络(CNN)与图像处理——让AI学会"看图说话" 引言:你的AI宠物如何认出猫狗? 想象你的手机突然有了"眼睛",不仅能识别照片里的猫狗,还能告诉你它们的品种&am…...
Ubuntu系统下Firefox浏览器完整指南:故障修复、国内版安装与下载加速
Ubuntu系统下Firefox浏览器完整指南:故障修复、国内版安装与下载加速 一、Firefox无法启动问题修复二、替换国际版安装国内版完整流程准备工作操作步骤验证要点 三、下载延迟问题解决方案现象分析优化配置步骤注意事项 四、进阶技巧补充五、常见问题FAQ 一、Firefox…...
【论文阅读一】掌握高效阅读法,开启学术研究新旅程:S. Keshav教授论文阅读的三遍法
文章目录 一、三遍阅读法1. 初读:10分钟:宏观把握,快速筛选2. 第二遍:1个小时:更仔细的阅读,了解文中论点3. 第三遍:深入理解,注重细节,挑战假设 二、运用三遍阅读法进行…...
多线程编程的常见问题
目录 1. 线程安全和可重入函数问题 2. 死锁的理解 2.1 死锁的概念 2.2 死锁的四个必要条件 3. C中STL容器的线程安全问题 4. C中智能指针的线程安全问题 1. 线程安全和可重入函数问题 线程安全:线程安全是指在多线程环境下,一个函数或者一段代码可…...
算法篇(九)【滑动窗口】
如果在分析一道算法题的时候,发现使用的两个 ”双指针“ , 都是同向的 , 不回退的 , 且一直都在维护 “一段连续的区间” , 此时我们可以考虑使用 “滑动窗口” ! 一、长度最小的子数组 209. 长度最小的子…...
【AI面试准备】传统测试工程师Prompt Engineering转型指南
介绍技能转型:传统测试工程师需掌握Prompt Engineering优化AI输出。如何快速掌握,以及在实际工作中如何运用。 传统测试工程师向AI时代的技能转型,掌握Prompt Engineering(提示工程)已成为提升工作效率、适应智能化测…...
数字智慧方案6186丨智慧应急指挥解决方案(43页PPT)(文末有下载方式)
资料解读:智慧应急指挥解决方案 详细资料请看本解读文章的最后内容。 在当今社会,各类突发事件频发,应急管理工作面临着巨大挑战。智慧应急指挥解决方案应运而生,旨在提升应急管理的效率和水平,保障人民生命财产安全。…...
d202552-sql
一、184. 部门工资最高的员工 - 力扣(LeetCode) 要找到每个部门工资最高的 使用窗口函数 加排序函数 排序函数用rank dense_rank都行 把最高相同的找出来就行 select *, dense_rank() over(partition by departmentId order by Salary desc) as rank …...
cpper 转 java
快速上手 java 特性 文章目录 java 语言特点JVM工作过程组成 java 语言特点 Java 程序编译成字节码,然后由 Java 虚拟机(JVM)执行 不同平台适配相同的 JVM ,从而使得 Java 程序具备跨平台性 —— 一次编写,到处运行 …...