Google Test 学习笔记(简称GTest)
文章目录
- 一、介绍
- 1.1 介绍
- 1.2 教程
- 二、使用
- 2.1 基本使用
- 2.1.1 安装GTest (下载和编译)
- 2.1.2 编写测试
- 2.1.3 运行测试
- 2.1.4 高级特性
- 2.1.5 调试和分析
- 2.2 源码自带测试用例
- 2.3 TEST 使用
- 2.3.1 TestCase的介绍
- 2.3.2 TEST宏
- demo1
- demo2
- 2.3.3 TEST_F宏
- 2.3.4 TEST 和 TEST_IF 区别
- 2.4 EXPECT_*和ASSERT_*的宏介绍
- 2.4.1 gtest之断言
- 2.4.2 Boolean断言类型
- 2.4.3 二元值断言类型
- 2.4.4 字符串断言类型
- 三、参考资料
- 四、其他内容
- 4.1 gtest 和 C++ 版本
一、介绍
1.1 介绍
Google Test(通常简称GTest)是Google开发的一个用于C++的单元测试框架,它可以帮助你轻松地编写和运行测试用例,确保代码的质量和稳定性。
- 最大好处:实现自动化单元测试
1.2 教程
- 官网:https://google.github.io/googletest/
- 源码:https://github.com/google/googletest
- 参考:gtest教程(记录小白从0学习gtest的过程)
- GoogleTest测试框架介绍(二)
- C++ 的测试框架之使用 gtest 编写单元测试
二、使用
- 编译库
- 编写工程代码
- 编写测试用例代码
- 工程:
- 工程源码:库 / 程序
- samples:示例程序,演示如何使用库的基本功能。
- test:gtest
2.1 基本使用
2.1.1 安装GTest (下载和编译)
1.下载源码:
访问Google Test GitHub仓库,下载或克隆源码。
2.编译GTest:
GTest可以通过CMake等构建工具来构建。以CMake为例,你需要创建一个构建目录,然后运行CMake和Make工具。
mkdir build
cd build
cmake ..
make
3.安装GTest:
将GTest的库文件和头文件复制到你的项目中,或者在你的构建系统中链接GTest库。
2.1.2 编写测试
1.包含GTest头文件:
在你的测试文件中,需要包含GTest的头文件。
#include "gtest/gtest.h"
2.定义测试用例和测试函数:
测试用例(TEST_F)通常对应于一组相关的测试函数,而测试函数(TEST)则是具体执行的测试逻辑。
TEST(FactorialTest, PositiveNumbers) { // 单独的测试函数EXPECT_EQ(1, Factorial(0));EXPECT_EQ(1, Factorial(1));EXPECT_EQ(2, Factorial(2));EXPECT_EQ(6, Factorial(3));
}class MyMathTest : public ::testing::Test {
protected:void SetUp() override {// 初始化工作}
};TEST_F(MyMathTest, TestAddition) {EXPECT_EQ(5, Add(2, 3));
}
3.断言:
使用GTest提供的断言来检查函数的行为是否符合预期。例如,EXPECT_EQ用于比较两个值是否相等。
4.测试驱动:
在main函数中调用::testing::InitGoogleTest和RUN_ALL_TESTS来初始化GTest并运行所有的测试。
int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}
2.1.3 运行测试
1.编译测试:
使用你的构建系统(如Makefile或CMakeLists.txt)来编译你的测试代码。
2.执行测试:
运行生成的可执行文件来执行测试。GTest会输出测试的结果,包括通过、失败或跳过的测试。
2.1.4 高级特性
- 参数化测试:使用INSTANTIATE_TEST_SUITE_P来创建一系列使用不同参数的测试用例。
- 死亡测试:使用ASSERT_DEATH或EXPECT_DEATH来检查代码是否会在特定条件下崩溃。
- Google Mock:GTest的一部分,用于创建和使用mock对象来进行更复杂的测试。
2.1.5 调试和分析
- 测试过滤:在运行测试时,可以使用–gtest_filter参数来指定运行哪些测试。
- 测试日志:使用–gtest_output=xml:test_results.xml等选项来生成测试报告。
2.2 源码自带测试用例
- 参考:https://gitcode.csdn.net/65acab6ab8e5f01e1e451947.html
我们还没有添加我们自己的源码,和针对源码的测试用例,但是谷歌已经写好了一些例子,可以先体验下,放在如下路径:/xxx/googletest-1.15.0/googletest
测试:
-
修改 CMakeLists.txt
# 这里 OFF 改成 ON option(gtest_build_samples "Build gtest's sample programs." OFF)相关编译内容:
if (gtest_build_samples)cxx_executable(sample1_unittest samples gtest_main samples/sample1.cc)cxx_executable(sample2_unittest samples gtest_main samples/sample2.cc)cxx_executable(sample3_unittest samples gtest_main)cxx_executable(sample4_unittest samples gtest_main samples/sample4.cc)cxx_executable(sample5_unittest samples gtest_main samples/sample1.cc)cxx_executable(sample6_unittest samples gtest_main)cxx_executable(sample7_unittest samples gtest_main)cxx_executable(sample8_unittest samples gtest_main)cxx_executable(sample9_unittest samples gtest)cxx_executable(sample10_unittest samples gtest) endif() -
编译:
mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX=pwd/result -DGOOGLETEST_VERSION=1.5.0 .. make -j4# 单个文件编译 # g++ ../src/gtest_main.cc sample1.cc sample1_unittest.cc -o test -lgtest -lgmock -lpthread -std=c++11 -
运行:
./sample1_unittest输出:

-
理解:
- gtest_main.cc :测试主程序入口,不是我们待测源码的主程序入口。
情况一:整个工程有两个main函数,一个是测试的main,一个可能是待测源码的main,两个包含main的文件不能同时编译,因为一个执行程序只能有一个入口。(可以n个文件n个main函数)
情况二:一个main函数通过宏定义区分启动 - sample1.cc:待测源码,就是测试对象,我们就是要对个源码进行白盒测试。
- sample1_unittest.cc:测试用例,里面就是我们针对源码写的测试用例脚本。
- gtest_main.cc :测试主程序入口,不是我们待测源码的主程序入口。
-
工程:
- 工程源码:库 / 程序
- samples:示例程序,演示如何使用库的基本功能。
- test:gtest
-
遗留问题:编译没看懂—>再看
2.3 TEST 使用
- 转自:Gtest入门2 Gtest之TEST宏的用法
2.3.1 TestCase的介绍
Gtest提供了若干个case方法进行测试不同的用例。主要常见的有TEST/TEST_F及TEST_P宏的使用。
在每个TestCase中可以通过断言提供的方法进行控制检查程序的预期走向是否是期望的结果,从而以此来判定程序的正确性。
在同一份TestCase中不能同时出现TEST和TEST_F两者进行混用;
其次TEST_F比TEST强的地方是会通过继承::testing::Test生成一个新类,而且这是必须的。
在新类中可以通过void SetUp();和void TearDown();进行创建和清除相关的资源数据;
2.3.2 TEST宏
TEST宏的作用是创建一个简单测试,它定义了一个测试函数,在这个函数里可以使用任何C++代码并使用提供的断言来进行检查。
TEST语法定义:
TEST(test_case_name, test_name)
- test_case_name第一个参数是测试用例名,通常是取测试函数名或者测试类名
- test_name 第二个参数是测试名这个随便取,但最好取有意义的名称
- 当测试完成后显示的测试结果将以"测试用例名.测试名"的形式给出
demo1
// test.cpp
// g++ -std=c++14 ../../src/gtest_main.cc test.cpp -o test -I../../include -L ../lib -lgtest -lpthread
#include <iostream>
#include <memory>
#include <gtest/gtest.h>using namespace std;class Base {
public:Base(std::string name):m_name{name} {std::cout << "name: " << m_name << std::endl;}std::string getName() {return m_name;}~Base() {std::cout << "destory base" << std::endl;}
private:std::string m_name;
};void getNameFunc(std::shared_ptr<Base> base) {std::cout << __func__ << " : usercount: " << base.use_count() << std::endl;std::cout << __func__ << " : name: " << base->getName() << std::endl;// EXPECT_EQ(2, base.use_count());
}TEST(Base, createInstance) {std::unique_ptr<Base> instance = make_unique<Base>("SvenBaseUnique");// 测试创建的instance实例是否不为nullptrEXPECT_NE(instance, nullptr);instance.reset();// 测试instance实例是否为nullptrEXPECT_EQ(instance, nullptr);
}TEST(Base, getName) {std::unique_ptr<Base> instance = make_unique<Base>("BaseUnique");EXPECT_NE(instance, nullptr);auto name = instance->getName();// 测试获取的name值是否和被给的值相等EXPECT_STREQ(name.c_str(), "BaseUnique");instance.reset();EXPECT_EQ(instance, nullptr);
}TEST(Base, shared_ptr) {std::shared_ptr<Base> instance = std::make_shared<Base>("BaseShared");EXPECT_NE(instance, nullptr);std::cout << "shared_ptr.use_count: " << instance.use_count() << std::endl;// 测试instance引用次数是否为1EXPECT_EQ(1, instance.use_count());getNameFunc(instance);EXPECT_EQ(1, instance.use_count());
}TEST(Base, unique_ptr) {std::unique_ptr<Base> instance = make_unique<Base>("BaseUnique");EXPECT_NE(instance, nullptr);getNameFunc(std::move(instance));EXPECT_EQ(instance, nullptr);
}
demo2
// test_2.cc
// g++ -std=c++14 test_2.cc -o test_2 -I../../include -L ../lib -lgtest -lpthread
#include <gtest/gtest.h>
#include <vector>
#include <string>
#include <cmath>// 示例函数,用于测试
int Add(int a, int b) {return a + b;
}std::vector<int> GetPrimesUnder100() {std::vector<int> primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97};return primes;
}class StringClass {
public:std::string Reverse(const std::string& s) {return std::string(s.rbegin(), s.rend());}
};// 测试类
class MyTest : public ::testing::Test {
protected:StringClass stringObj;
};// 测试用例:测试 Add 函数
TEST(AdditionTest, HandlesZeroInput) {EXPECT_EQ(Add(0, 0), 0);ASSERT_EQ(Add(0, 1), 1);// EXPECT_EQ(Add(0, 0), 30); // error test// ASSERT_EQ(Add(0, 1), 30);// ASSERT_EQ(Add(1, 1), 2);
}TEST(AdditionTest, HandlesPositiveInput) {EXPECT_EQ(Add(2, 3), 5);ASSERT_EQ(Add(-1, 1), 0);
}TEST(AdditionTest, HandlesNegativeInput) {EXPECT_EQ(Add(-1, -1), -2);ASSERT_EQ(Add(-2, 3), 1);
}// 测试用例:测试 GetPrimesUnder100 函数
TEST(PrimeTest, ReturnsCorrectPrimes) {auto primes = GetPrimesUnder100();ASSERT_EQ(primes.size(), 25);EXPECT_EQ(primes[0], 2);EXPECT_EQ(primes[24], 97);
}// 测试用例:测试 StringClass 的 Reverse 方法
TEST_F(MyTest, ReversesString) {std::string original = "hello";std::string reversed = stringObj.Reverse(original);EXPECT_EQ(reversed, "olleh");
}TEST_F(MyTest, ReversesEmptyString) {std::string original = "";std::string reversed = stringObj.Reverse(original);EXPECT_EQ(reversed, "");
}TEST_F(MyTest, ReversesSingleCharacterString) {std::string original = "a";std::string reversed = stringObj.Reverse(original);EXPECT_EQ(reversed, "a");
}// 测试用例:测试浮点数比较
TEST(FloatTest, ComparesFloats) {float a = 1.0f + 1e-5f;float b = 1.0f;EXPECT_NEAR(a, b, 1e-4f);
}// 测试用例:测试布尔值
TEST(BooleanTest, ChecksTrue) {bool condition = true;EXPECT_TRUE(condition);ASSERT_TRUE(condition);
}TEST(BooleanTest, ChecksFalse) {bool condition = false;EXPECT_FALSE(condition);ASSERT_FALSE(condition);
}// 测试用例:测试异常
TEST(ExceptionTest, ThrowsException) {ASSERT_THROW(throw std::runtime_error("test exception"), std::runtime_error);
}// 测试用例:测试子测试
TEST(SubtestTest, Subtest1) {SUCCEED() << "This is a subtest.";EXPECT_EQ(1, 1);
}TEST(SubtestTest, Subtest2) {SUCCEED() << "This is another subtest.";EXPECT_EQ(2, 2);
}int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}
2.3.3 TEST_F宏
TEST_F主要是进行多样测试,就是多种不同情况的测试TestCase中都会使用相同一份的测试数据的时候将会才用它。
即用相同的数据测试不同的行为,如果采用TEST宏进行测试那么将会为不同的测试case创建一份数据。TEST_F宏将会共用一份避免重复拷贝共具灵活性。
语法定义为:
TEST_F(test_case_name, test_name);
- test_case_name第一个参数是测试用例名,必须取类名。这个和TEST宏不同
- test_name 第二个参数是测试名这个随便取,但最好取有意义的名称
- 使用TEST_F时必须继承::testing::Test类。并且该类提供了两个接口void SetUp(); void TearDown();
void SetUp()函数,为测试准备对象. void TearDown()函数 为测试后销毁对象资源
2.3.4 TEST 和 TEST_IF 区别
在 Google Test 中,TEST 和 TEST_F 是用来定义测试用例的两个不同的宏。它们之间的主要区别在于是否需要测试夹具(test fixture):
-
TEST(SuiteName, TestName)
TEST宏用于定义不需要特殊初始化或清理的简单测试用例。- 它不接受任何参数,因此也不需要测试夹具类。
- 测试用例函数是自动实例化的,并且每个测试用例都是独立的,不共享任何设置。
- 适用于快速、独立的测试,不需要重复的初始化逻辑。
示例代码:
TEST(MultiplicationTest, HandlesZero) {EXPECT_EQ(0 * 5, 0); } -
TEST_F(TestFixture, TestName)
TEST_F宏用于定义需要使用测试夹具的测试用例。- 测试夹具是一个从
::testing::Test派生的类,你可以在里面定义公共和受保护的成员,这些成员在每个测试用例中都可以访问。 - 测试夹具类允许你在
SetUp方法中编写初始化代码,这些代码会在每个测试用例运行之前执行,而在TearDown方法中编写清理代码,这些代码会在每个测试用例运行之后执行。 - 适用于需要共享初始化逻辑和成员数据的测试用例。
示例代码:
class MultiplicationTest : public ::testing::Test { protected:int result;void SetUp() override {result = 0;}void TearDown() override {// 清理工作,如果需要的话} };TEST_F(MultiplicationTest, HandlesZero) {result = 0 * 5;EXPECT_EQ(result, 0); }
除了 TEST 和 TEST_F,Google Test 还有一个宏 TEST_P,它用于定义参数化测试。参数化测试允许你用不同的参数多次运行同一个测试用例。
关于 TEST_IF 宏,实际上 Google Test 标准库中并没有这个宏。可能你指的是 TEST_P 或者某个特定版本的 Google Test 中的宏,或者是第三方扩展。通常情况下,TEST_P 用于参数化测试,允许你为测试用例提供参数,并且可以结合 INSTANTIATE_TEST_CASE_P 宏来实例化多个测试用例。
如果你需要更具体的信息或者有特定的使用场景,请提供更多的上下文,我可以给出更准确的答案。
2.4 EXPECT_*和ASSERT_*的宏介绍
- 转自:Gtest入门2 Gtest之TEST宏的用法
2.4.1 gtest之断言
要测试一个类或函数,我们需要对其行为做出断言。当一个断言失败时,Google Test会在屏幕上输出该代码所在的源文件及其所在的位置行号,以及错误信息。也可以在编写断言时,提供一个自定义的错误信息,这个信息在失败时会被附加在Google Test的错误信息之后。
断言常常成对出现,它们都测试同一个类或者函数,但对当前功能有着不同的效果。
- ASSERT_*版本的断言失败时会产生致命失败,并结束当前函数。
- EXPECT_*版本的断言产生非致命失败,而不会中止当前函数。
通常更推荐使用EXPECT_*断言,因为它们运行一个测试中可以有不止一个的错误被报告出来。但如果在编写断言如果失败,就没有必要继续往下执行的测试时,你应该使用ASSERT_*断言。 因为失败的ASSERT_*断言会立刻从当前的函数返回,可能会跳过其后的一些的清洁代码,这样也许会导致空间泄漏。
gtest中断言的宏可以分为两类:一类是ASSERT宏,另一类就是EXPECT宏了。
1、ASSERT_*系列:如果当前点检测失败则退出当前函数
2、EXPECT_*系列:如果当前点检测失败则继续往下执行
2.4.2 Boolean断言类型

2.4.3 二元值断言类型
比较两个值的大小。

2.4.4 字符串断言类型
比较两个字符串。

三、参考资料
-
玩转C++单元测试之快速上手gtest
-
Google Test(GTEST)使用入门(1)- 下载编译安装执行
四、其他内容
4.1 gtest 和 C++ 版本
Google Test(GTest)对不同版本的 C++ 标准的支持有所变化。下面是 Google Test 对 C++ 标准版本支持的大致时间线和相关信息。
Google Test 对 C++ 标准的支持
-
GTest 1.8.1(发布于 2015 年 7 月 29 日):
- 支持 C++98、C++03、C++11、C++14。
- 从 GTest 1.8.1 开始,GTest 支持 C++14,并且开始逐渐移除对旧版本 C++ 标准的支持。
-
GTest 1.10.0(发布于 2019 年 10 月 28 日):
- 支持 C++11、C++14、C++17。
- GTest 1.10.0 版本开始正式移除对 C++98 和 C++03 的支持。
-
GTest 1.11.0(发布于 2021 年 1 月 25 日):
- 支持 C++11、C++14、C++17。
- GTest 1.11.0 版本继续支持 C++11、C++14 和 C++17。
-
GTest 1.12.1(发布于 2022 年 8 月 17 日):
- 支持 C++11、C++14、C++17、C++20。
- GTest 1.12.1 版本增加了对 C++20 的支持。
-
GTest 1.13.0(发布于 2023 年 11 月 2 日):
- 支持 C++14、C++17、C++20。
- GTest 1.13.0 版本移除了对 C++11 的支持,现在仅支持 C++14 及以上版本。
总结
-
最新版本(截至 2024 年 7 月 25 日):
- GTest 1.13.0 支持 C++14、C++17 和 C++20。
-
历史版本:
- GTest 1.12.1 支持 C++11、C++14、C++17 和 C++20。
- GTest 1.11.0 支持 C++11、C++14 和 C++17。
- GTest 1.10.0 支持 C++11、C++14 和 C++17。
- GTest 1.8.1 支持 C++98、C++03、C++11 和 C++14。
建议
-
使用最新版本:
- 如果您的项目可以使用较新的 C++ 版本,建议使用 GTest 1.13.0 或更高版本,以获得更好的特性和支持。
- 对于 C++14 及以上版本的支持,您可以使用 GTest 1.13.0。
-
使用较旧版本:
- 如果您的项目受限于旧版本的 C++,您可以考虑使用相应的 GTest 版本。例如,如果您的项目使用 C++11,您可以考虑使用 GTest 1.12.1 或更低版本。
示例编译命令
如果您使用的是 GTest 1.13.0 或更高版本,并且您的编译器支持 C++14 或更高版本,您可以使用以下编译命令:
g++ -std=c++14 ../../src/gtest_main.cc test.cpp -o test -I../../include -L ../lib -lgtest -lpthread
请确保您的编译器支持所需的 C++ 标准版本。如果您的编译器不支持 C++14 或更高版本,您可能需要升级编译器或考虑使用较低版本的 GTest。
如果您需要使用 C++11,您可以使用 GTest 1.12.1 或更低版本,并相应地更新编译命令中的 C++ 标准版本:
g++ -std=c++11 ../../src/gtest_main.cc test.cpp -o test -I../../include -L ../lib -lgtest -lpthread
请根据您的具体需求调整编译命令中的 C++ 标准版本。
相关文章:
Google Test 学习笔记(简称GTest)
文章目录 一、介绍1.1 介绍1.2 教程 二、使用2.1 基本使用2.1.1 安装GTest (下载和编译)2.1.2 编写测试2.1.3 运行测试2.1.4 高级特性2.1.5 调试和分析 2.2 源码自带测试用例2.3 TEST 使用2.3.1 TestCase的介绍2.3.2 TEST宏demo1demo2 2.3.3 TEST_F宏2.3…...
不可变集合
定义:就是集合中的内容不可以被修改。 如何获取不可变集合? List、Set、Map类中提供的静态方法of可用来获取不可变集合。 特点:一旦创建完成只可以进行查询,不可以增删改。 细节:Map集合中的of方法只能添加10个键值…...
景区AR导航营销系统:技术解决方案与实施效益分析
随着旅游市场的竞争日益激烈,景区需要不断创新以吸引游客。景区 AR 导航将虚拟画面与现实场景相结合,为游客提供了更加直观、生动的导航服务。对于景区而言,这一创新技术无疑是吸引游客目光、提升景区知名度的有力武器。通过独特的 AR 导航体…...
MATLAB的基础知识
matlab的基本小常识 1. 在每行语句后面加上英文分号表示不在命令行窗口显示运行结果。 a 3; a 5 2. 多行注释快捷键,CTRLR。 3. 取消多行注释,CTRLT。 4. 清空工作区的所有变量使用clear。 5. 清空命令行窗口的所有变量使用clc。 6. clc和clear一起使…...
Redis-高级实战案例
文章目录 Redis集群崩溃时如何保证秒杀系统高可用1. 冗余与备份2. 故障检测与自动切换3. 降级策略4. 数据一致性5. 客户端缓存6. 异常处理与通知7. 测试与演练8. 服务降级与回滚Redis主从切换导致库存同步异常以及超卖问题主从切换导致的库存同步异常原因:解决方案:秒杀链路中…...
d3d12.dll 文件缺失如何解决?五种修复丢失问题的方法
d3d12.dll 文件缺失如何解决?它为什么会不见呢?今天,我们将探讨 d3d12.dll 文件的重要性、原因以及丢失时的解决策略。本文将全面介绍 d3d12.dll 文件,并提供五种修复丢失问题的方法。 d3d12.dll文件是什么的详细介绍 d3d12.dll …...
Linux下如何设置系统定时任务
在Linux系统中,用户可以使用cron工具来设置定时任务。cron是一个守护进程,用于在指定的时间间隔执行指定的命令或脚本。下面是在Linux系统中设置系统定时任务的步骤。 使用crontab命令编辑定时任务列表: crontab -e该命令会打开一个文本编辑…...
【React】JSX 实现列表渲染
文章目录 一、基础语法1. 使用 map() 方法2. key 属性的使用 二、常见错误和注意事项1. 忘记使用 key 属性2. key 属性的选择 三、列表渲染的高级用法1. 渲染嵌套列表2. 条件渲染列表项3. 动态生成组件 四、最佳实践 在 React 开发中,列表渲染是一个非常常见的需求。…...
写一个简单的兼容GET/POST请求的登录接口
本文目录 安装JDK17安装或者更新Intelij Idea 2024SpringBoot生成项目压缩包下载maven,idea添加maven写POST接口浏览器访问GET接口PostMan安装及访问POST接口 安装JDK17 参考:https://blog.csdn.net/tiehou/article/details/129575138 安装或者更新Int…...
【好玩的经典游戏】Docker环境下部署赛车小游戏
【好玩的经典游戏】Docker环境下部署赛车小游戏 一、小游戏介绍1.1 小游戏简介1.2 项目预览二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 安装Docker环境3.2 检查Docker服务状态3.3 检查Docker版本3.4 检查docker compose 版本四、构建容器镜像4.1 下…...
物理机 gogs+jenkins+sonarqube 实现CI/CD
一、部署gogs_0.11.91_linux_amd64.tar.gz gogs官网下载:https://dl.gogs.io/ yum -y install mariadb-serversystemctl start mariadbsystemctl enable mariadbuseradd gittar zxvf gogs_0.11.91_linux_amd64.tar.gzcd gogsmysql -u root -p < scripts/mysql.…...
前端表格解析方法
工具类文件 // fileUtils.tsimport { ref } from vue; import * as xlsx from xlsx;interface RowData {[key: string]: any; }export const tableData ref<RowData[]>([]);export async function handleFileSelect(url: string): Promise<void> {try {const res…...
Leetcode 3227. Vowels Game in a String
Leetcode 3227. Vowels Game in a String 1. 解题思路2. 代码实现 题目链接:3227. Vowels Game in a String 1. 解题思路 这一题稍微分析一下之后就会发现,这个游戏有且只有一种情况Bob才能够赢,即原始字符串当中不存在元音字母的情况&…...
树莓派4B从装系统raspbian到vscode远程编程(python)
1、写在前面 前面用的一直是Ubuntu系统,但是遇到一个奇葩的问题: 北通手柄在终端可以正常使用,接收到数据 但在python程序中使用pygame库初始化时总是报错:Invalid device number,检测不到手柄 经过n次重装系统&am…...
vue上传Excel文件并直接点击文件列表进行预览
本文主要内容:用elementui的Upload 组件上传Excel文件,上传后的列表采用xlsx插件实现点击预览表格内容效果。 在项目中可能会有这样的需求,有很多种方法实现。但是不想要跳转外部地址,所以用了xlsx插件来解析表格,并展…...
OpenCV 像素操作—证件照换底色详细原理 C++纯手写实现
文章目录 总体步骤1.RGB转HSV2.找出要换的底色3.取反,黑白颠倒4.将原图像的非背景部分复制到新背景上 完整代码1.C纯手写版2.官方API版本 总体步骤 1.RGB转HSV 为什么一定要转为HSV 颜色空间? 将图像从BGR颜色空间转换为HSV颜色空间是因为HSV颜色空间更…...
tinygrad框架简介;MLX框架简介
目录 tinygrad框架简介 MLX框架简介 LLaMA编辑 Stable Diffusion编辑 tinygrad框架简介 极简主义与易扩展性 tinygrad 的设计理念是极简主义。与 XLA 类比,如果 XLA 是复杂指令集计算 (CISC),那么 tinygrad 就是精简指令集计算 (RISC)。这种简约的设计使得它成为添加…...
服务器重启了之后就卡在某个页面了,花屏,如何解决??
🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收…...
Hospital 14.6.0全开源医院管理预约系统源码
InfyHMS 具有 60 种功能和 9 种不同类型的用户类型, 他们可以登录系统并根据他们的角色访问他们的数据。 源码下载:https://download.csdn.net/download/m0_66047725/89580674 更多资源下载:关注我。...
C/C++樱花树代码
目录 写在前面 系列文章 C简介 完整代码 代码分析 写在后面 写在前面 C实现精美的樱花树,只需这100行代码! 系列文章 序号目录直达链接1爱心代码https://want595.blog.csdn.net/article/details/1363606842李峋同款跳动的爱心https://want595.b…...
从USB转TTL接线到手机热点配网:ESP8266无线通信保姆级避坑指南(附软件包)
从USB转TTL接线到手机热点配网:ESP8266无线通信保姆级避坑指南 当你第一次拿起ESP8266模块时,可能会被这个小巧的Wi-Fi模块惊艳到——它只有指甲盖大小,却蕴含着强大的无线通信能力。但很快,这种惊艳就会变成困惑:为什…...
2026上半年数据库系统工程师(软考)上午题回忆与解析(非标答版)
本文为考后回忆整理,非官方标准答案,旨在为考后对答案及下半年备考的同学提供参考。题目顺序和表述可能与原卷有出入,欢迎在评论区指正、补充。📊 整体考情分析 刚结束的2026年上半年数据库系统工程师考试,上午题的风格…...
开发转兼职DBA(二):执行计划教我做事
开发转兼职DBA(二):执行计划教我做事 查询慢了不知道为什么,加了索引还是慢,复合索引怎么建,执行计划怎么看——这些不是DBA的专利,是每个写SQL的开发者迟早要面对的事。 文章目录 开发转兼职DB…...
基于Arduino的智能蓝调节拍器:DIY音乐练习伴侣
1. 项目概述:一个能“演奏”蓝调的低成本节拍器玩乐器的人,对节拍器这东西又爱又恨。它像一位严厉的监工,用单调的“嘀嗒”声强迫你跟上节奏。但你想过没有,这个监工其实可以很有趣?几年前,我在练习蓝调吉他…...
rk35xx 通过recovery升级问题
Firefly 的 recovery 库是一个核心组件,它构建了一个独立的微型 Linux 系统,专门用于在设备主系统之外执行高可靠性的固件升级。简单来说,它的工作流程是:主系统通过命令触发,将升级指令写入特定分区并重启;…...
为Claude Code配置稳定API源并解决访问限制
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 为Claude Code配置稳定API源并解决访问限制 Claude Code 作为一款强大的 AI 编程辅助工具,其原生服务在某些情况下可能…...
手把手教你用Mind+和Blynk,让手机轻松遥控掌控板(含自建服务器避坑指南)
从零搭建物联网控制平台:Mind与Blynk深度整合实战 当你第一次尝试用手机控制硬件设备时,那种"隔空取物"的奇妙感总会让人兴奋不已。想象一下,躺在沙发上就能调节书桌上的智能台灯亮度,或者在外出时随时查看家中的温湿度…...
具身智能:面向新兴交叉学科建设的思考与建议 2026
这份由 CCF YOCSEF 长三角五地学术委员会 2026 年 5 月发布的白皮书,聚焦具身智能作为新兴交叉学科的建设,明确其并非 AI 与机器人学的简单拼接,而是围绕物理交互中的智能行为形成的新问题域,提出 “三大基本问题 一个应用需求”…...
PS5 NOR Modifier深度解析:如何通过Windows工具修复PS5硬件故障与实现光驱版转数字版
PS5 NOR Modifier深度解析:如何通过Windows工具修复PS5硬件故障与实现光驱版转数字版 【免费下载链接】PS5NorModifier The PS5 Nor Modifier is an easy to use Windows based application to rewrite your PS5 NOR file. This can be useful if your NOR is corru…...
Claude Code用户告别封号与Token焦虑,无缝切换至Taotoken平台
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Claude Code用户告别封号与Token焦虑,无缝切换至Taotoken平台 对于依赖Claude Code进行编程辅助的开发者而言ÿ…...
