C++ String类模拟实现:从内存管理到STL兼容的完整指南
1. 项目概述为什么要亲手实现一个String类在C的世界里std::string大概是每个开发者最早接触、使用最频繁的STL组件之一。从简单的“Hello World”到复杂的文本处理它无处不在。正因为它如此基础和重要很多面试官喜欢把它作为考察候选人C基本功的“试金石”。你可能会背出它的常用接口知道size()、append()、find()怎么用但你是否想过这个看似简单的字符串类内部是如何管理内存的拷贝一个字符串时底层发生了什么为什么有些操作会比你预想的要慢这就是“模拟实现”的价值所在。它不是一个简单的重复造轮子练习而是一次深入系统底层、理解C核心思想的绝佳机会。通过亲手从零构建一个MyString类你将被迫直面以下问题如何高效地管理动态内存以避免内存泄漏如何设计拷贝控制成员拷贝构造、拷贝赋值、析构来实现资源的正确管理如何权衡性能与安全性设计出高效的接口这个过程会让你对RAII资源获取即初始化、深浅拷贝、移动语义等概念有刻骨铭心的理解其收获远超过阅读十篇理论文章。我当年在准备面试和夯实基础时就完整实现过好几次String类每次都有新的体会。这次我将带你一起从一个空类开始逐步构建一个功能完整、考虑边界情况、并具有一定工业级雏形的String类。我们会聚焦于最核心的成员函数并深入探讨其设计背后的“为什么”。2. 核心设计思路与类框架搭建2.1 确定核心数据成员与资源管理策略一个字符串类的本质是管理一段动态分配的、用于存放字符序列的堆内存。因此最核心的数据成员通常是一个指向字符数组的指针char* _str。仅有一个指针够吗在实际应用中我们经常需要快速获取字符串长度如果每次调用size()都去遍历字符串直到遇到\0空字符时间复杂度是O(n)这在频繁查询长度的场景下是不可接受的。因此我们通常需要额外维护一个表示字符串当前长度的成员size_t _size。那容量capacity呢容量指的是当前已分配的内存最多能容纳多少字符不包括结尾的\0。为了减少频繁扩容带来的性能损耗每次扩容都可能涉及申请新内存、拷贝数据、释放旧内存我们通常会采用“预分配”策略即一次分配比当前需求稍大的内存。因此记录容量的成员size_t _capacity也很有必要。这就是经典的“size-capacity”模型。于是我们类的私有成员初步确定如下class MyString { private: char* _str; // 指向堆上字符数组的指针 size_t _size; // 当前字符串有效长度不含\0 size_t _capacity; // 当前分配的总容量不含\0 // ... 后续可能添加的成员如引用计数用于写时复制等 };关于资源管理我们必须遵循RAII原则构造函数获取资源析构函数释放资源。这意味着我们必须在每个构造函数中正确地初始化或分配内存并在析构函数中无条件地释放_str指向的内存。2.2 基础构造函数与析构函数实现构造函数是对象诞生的起点。我们需要考虑几种常见的构造场景默认构造空字符串、用C风格字符串构造、用另一个MyString对象构造拷贝构造。1. 默认构造函数它的任务是创建一个空的MyString对象。一个健壮的空字符串不应该让_str是空指针nullptr因为很多操作如operator[]在_str为nullptr时访问会引发未定义行为。通常的做法是为_str分配一个字节的内存并存入一个\0。MyString::MyString() : _size(0) , _capacity(0) { _str new char[1]; // 分配1字节 _str[0] \0; // 设置为空字符串 }这里_capacity设为0是因为我们只分配了存放\0的空间没有多余容量。也可以选择初始分配一个小的缓冲区如16字节将_capacity设为15留一个给\0这是一种空间换时间的优化可以减少初始阶段添加字符时的扩容次数。具体选择取决于你的性能预期。2. 用C风格字符串构造这是最常用的构造方式之一。我们需要计算传入的C字符串的长度然后分配足够的内存长度1用于存放\0最后进行拷贝。MyString::MyString(const char* str) : _size(strlen(str)) , _capacity(_size) { _str new char[_capacity 1]; // 多分配1字节给\0 strcpy(_str, str); // 包括\0一起拷贝 }这里有一个关键点我们假设str是有效的、以\0结尾的字符串。在工业级代码中需要对str进行空指针检查如果为nullptr可以将其视为空字符串处理或者抛出异常。3. 拷贝构造函数这是C面试的经典考点也是资源管理类的核心。它的功能是用一个已存在的MyString对象来初始化一个新对象。这里必须进行“深拷贝”即复制内容而不是复制指针。MyString::MyString(const MyString other) : _size(other._size) , _capacity(other._capacity) { _str new char[_capacity 1]; strcpy(_str, other._str); // 深拷贝内容 }如果只是简单地进行指针赋值_str other._str那就是“浅拷贝”两个对象会共享同一块内存析构时会导致同一块内存被释放两次引发程序崩溃。这就是著名的“双杀”问题。4. 析构函数析构函数的职责非常单一释放构造函数中申请的资源。MyString::~MyString() { delete[] _str; // 释放字符数组 _str nullptr; // 避免野指针这是一个好习惯 _size _capacity 0; }注意delete和delete[]必须严格匹配。我们用new char[...]分配数组就必须用delete[]释放。混用会导致未定义行为。2.3 关键辅助函数扩容Reserve与调整大小Resize在实现增删改查等操作前我们需要两个底层工具函数来管理内存。1. Reserve 函数Reserve的功能是确保字符串的容量至少为n。它只影响容量不改变字符串的内容和长度_size。如果请求的容量n大于当前容量_capacity就需要重新分配内存。void MyString::reserve(size_t n) { if (n _capacity) { char* newStr new char[n 1]; // 分配新内存1给\0 strcpy(newStr, _str); // 拷贝原有数据 delete[] _str; // 释放旧内存 _str newStr; _capacity n; // 注意newStr[n] 位置是未初始化的我们保证它在_size范围内是\0但尾部可能有垃圾字符。 // 更安全的做法是显式设置 newStr[_size] \0但strcpy已经做了。 } // 如果 n _capacity什么都不做 }这里有一个性能优化点内存分配new是相对昂贵的操作。因此常见的策略是当需要扩容时不是精确地扩容到n而是按一定比率例如1.5倍或2倍进行扩容以减少扩容次数。这就是std::vector和std::string通常采用的策略。我们可以在push_back或append等函数内部调用一个私有的_grow_if_needed函数来实现这种几何增长。2. Resize 函数Resize的功能是改变字符串的有效长度_size。它可能涉及容量的扩展并且会改变字符串的内容。如果new_size _size只需将_size设为new_size并在_str[new_size]位置放入\0实现截断。如果new_size _size需要先确保容量足够可能调用reserve然后将新增的部分从原_size到new_size-1填充为指定的字符ch默认为\0最后在_str[new_size]位置放入\0。void MyString::resize(size_t n, char ch \0) { if (n _size) { _size n; _str[_size] \0; // 截断 } else { reserve(n); // 确保容量足够 for (size_t i _size; i n; i) { _str[i] ch; // 填充字符 } _size n; _str[_size] \0; // 设置新结尾 } }3. 核心功能接口的模拟实现有了内存管理的基础我们就可以实现那些让字符串类变得好用的功能接口了。3.1 赋值运算符重载现代C写法的精髓赋值运算符operator同样需要深拷贝并且要处理好自赋值str str的情况。传统的写法是MyString MyString::operator(const MyString other) { if (this ! other) { // 1. 防止自赋值 delete[] _str; // 2. 释放原有资源 _size other._size; _capacity other._capacity; _str new char[_capacity 1]; // 3. 申请新资源 strcpy(_str, other._str); // 4. 拷贝数据 } return *this; // 5. 返回本对象引用以支持链式赋值 }这个写法是安全的但不够优雅而且new可能会失败抛出异常如果发生在释放旧资源之后对象就会处于一个资源已释放但新资源未获取的无效状态。更现代、更安全的写法是“拷贝-交换”惯用法Copy-and-Swap Idiom。它利用拷贝构造函数创建一个临时对象然后交换当前对象和临时对象的内容。临时对象在析构时会自动清理掉旧资源。MyString MyString::operator(MyString other) { // 注意参数是值传递会调用拷贝构造 swap(other); // 交换当前对象和临时对象的内容 return *this; } // 临时对象other现在持有原对象的旧资源离开作用域被析构这种方法异常安全代码简洁还自动处理了自赋值因为参数是值传递自赋值时也会先拷贝一份副本。但它需要一个高效的swap成员函数void MyString::swap(MyString other) noexcept { std::swap(_str, other._str); std::swap(_size, other._size); std::swap(_capacity, other._capacity); }3.2 元素访问与迭代器1. 下标访问运算符operator[]我们需要提供常量版本和非常量版本以支持对字符串的读和写操作。// 非常量版本允许修改 char MyString::operator[](size_t pos) { // 通常进行边界检查这里我们断言。生产环境可能抛出异常。 assert(pos _size); return _str[pos]; } // 常量版本只读 const char MyString::operator[](size_t pos) const { assert(pos _size); return _str[pos]; }提供const重载是良好的习惯它使得const MyString对象也能使用[]运算符尽管不能修改。2. 迭代器为了让MyString能兼容STL算法如std::sort,std::find我们通常提供迭代器。最简单的方式是直接使用指针作为迭代器。// 在类定义中添加类型别名 typedef char* iterator; typedef const char* const_iterator; iterator begin() { return _str; } iterator end() { return _str _size; } // 指向\0符合STL“尾后迭代器”约定 const_iterator begin() const { return _str; } const_iterator end() const { return _str _size; } const_iterator cbegin() const { return _str; } const_iterator cend() const { return _str _size; }这样我们就可以像使用数组或vector一样使用范围for循环for (char ch : myStr) { ... }。3.3 容量与操作相关函数这些函数实现相对直接但要注意细节。size(),length(),capacity(): 直接返回对应成员变量。empty(): 判断_size 0。clear(): 将_size设为0并在_str[0]位置放入\0。注意它通常不释放内存_capacity不变这是为了后续可能的重用符合STL容器的习惯。c_str(): 返回_str。这是一个“观察器”返回内部指针调用者不应通过它修改内容或释放内存。push_back(char ch): 在末尾添加一个字符。需要先检查容量是否足够if (_size _capacity) reserve(...)然后将字符放入_str[_size]_size最后设置_str[_size] \0。append(const char* str): 追加一个C字符串。计算追加字符串的长度len检查当前容量是否足够容纳_size len不够则扩容然后用strcpy或memcpy将字符串拷贝到_str _size的位置最后更新_size。append的实现是许多其他操作如operator的基础务必保证其正确性。3.4 字符串操作查找、比较与子串1. 查找find实现一个查找函数需要考虑很多边界情况。我们实现一个从位置pos开始查找字符ch的版本。size_t MyString::find(char ch, size_t pos 0) const { if (pos _size) return npos; // 起始位置越界 const char* result strchr(_str pos, ch); // 使用标准库函数 return result ? result - _str : npos; }这里使用了strchr这个C标准库函数。我们需要在类中定义一个静态常量npos通常设为(size_t)-1表示未找到。查找子串的函数find(const char* sub)实现更复杂可以使用strstr函数但同样要注意边界检查和npos的处理。2. 比较compare字符串比较可以基于strcmp实现。int MyString::compare(const MyString other) const { return strcmp(_str, other._str); } // 重载比较运算符 bool operator(const MyString lhs, const MyString rhs) { return lhs.compare(rhs) 0; } bool operator(const MyString lhs, const MyString rhs) { return lhs.compare(rhs) 0; } // ... 其他运算符类似将比较运算符重载为全局函数可以支持MyString与const char*等类型的混合比较通过隐式转换或额外重载。3. 获取子串substrsubstr需要从指定位置pos开始截取长度为len的子串如果len过大则截取到字符串末尾。它需要创建一个新的MyString对象。MyString MyString::substr(size_t pos, size_t len npos) const { if (pos _size) { // 可以抛出异常或返回空字符串 return MyString(); } size_t actualLen len; if (len npos || pos len _size) { actualLen _size - pos; // 截取到末尾 } MyString subStr; subStr.reserve(actualLen); for (size_t i 0; i actualLen; i) { subStr.push_back(_str[pos i]); } return subStr; // 依赖拷贝构造或移动构造如果实现了 }这里注意npos被用作默认参数表示“直到末尾”。在C11之后如果实现了移动构造函数返回局部对象subStr会触发移动语义避免一次深拷贝效率更高。4. 进阶话题与性能优化考量4.1 实现移动语义C11在C11之前返回一个局部对象如上面的substr必然触发拷贝构造如果字符串很大开销不小。移动构造函数和移动赋值运算符允许我们“偷走”临时对象右值的资源。// 移动构造函数 MyString::MyString(MyString other) noexcept : _str(other._str) , _size(other._size) , _capacity(other._capacity) { // 将源对象置于有效但可析构的状态 other._str nullptr; other._size other._capacity 0; } // 移动赋值运算符 MyString MyString::operator(MyString other) noexcept { if (this ! other) { delete[] _str; // 释放自身原有资源 _str other._str; _size other._size; _capacity other._capacity; other._str nullptr; other._size other._capacity 0; } return *this; }移动操作不分配新内存只是接管指针并将源对象置空效率极高。实现了移动语义后之前的“拷贝-交换”赋值运算符会自动受益因为参数是值传递当传入右值时会优先调用移动构造来创建参数other。4.2 探讨写时复制Copy-On-Write写时复制是一种优化技术旨在减少不必要的深拷贝。其核心思想是多个对象可以共享同一份数据通过一个引用计数管理只有当某个对象需要修改数据时才真正进行拷贝。这可以极大提升以读为主、拷贝频繁场景下的性能。实现COW的MyString需要将数据指针和引用计数封装在一起通常放在堆上另一个结构体中。拷贝构造和拷贝赋值不再深拷贝数据而是递增引用计数并共享指针。只有在非const的operator[]、append等可能修改数据的操作前才检查引用计数如果计数大于1则进行真正的拷贝“写时”拷贝。然而COW的实现复杂度高需要仔细处理线程安全引用计数的原子操作并且在现代多线程环境和移动语义普及后其性能优势有时并不明显甚至可能因原子操作和分支判断而变慢。std::string在C11后通常不再使用COW部分原因是为了保证迭代器在多线程下的安全性。了解COW有助于理解一种重要的优化思想但在自己实现时需权衡利弊。4.3 迭代器失效问题这是一个非常重要且容易出错的问题。当对字符串进行修改操作如insert,erase,append导致扩容时指向其内部数据的指针、引用和迭代器可能会失效。导致失效的操作任何可能引起内存重新分配的操作如reserve,append当容量不足时insert当容量不足时。不导致失效的操作push_back如果容量足够、operator[]非扩容情况下的读写。例如MyString str hello; char* p str[0]; // 获取指向首字符的指针 str.append( world, this is a long string that may cause reallocation); // 此时p 可能已经是一个悬垂指针因为append可能导致str扩容_str指向了新内存。 *p H; // 未定义行为在自己的代码中要清楚每个操作可能带来的副作用并在文档中明确说明。使用迭代器时最好在可能引发扩容的操作后重新获取迭代器。5. 测试、常见问题与调试技巧5.1 构建完整的测试用例实现完成后必须进行系统测试。测试应覆盖以下场景基础功能默认构造、C字符串构造、拷贝构造、赋值、析构。边界条件空字符串操作、下标越界访问用assert或异常测试、查找不到返回npos。自我操作自赋值str str、自我追加str.append(str.c_str())。内存管理重复多次进行字符串构造、拷贝、赋值、销毁使用工具如Valgrind检查是否有内存泄漏。性能粗略评估使用大字符串测试append和拷贝操作的耗时观察是否符合预期例如扩容是否呈几何级数增长。一个简单的测试框架可以这样写void TestMyString() { // 1. 构造与基本功能 MyString s1; assert(s1.empty() s1.size() 0); MyString s2 Hello; assert(s2.size() 5); assert(s2[0] H); // 2. 拷贝与赋值 MyString s3 s2; // 拷贝构造 assert(s3 s2); s1 s3; // 拷贝赋值 assert(s1 s2); // 3. 修改操作 s2.push_back(!); assert(s2 Hello!); // 4. 查找与子串 assert(s2.find(e) 1); MyString sub s2.substr(1, 3); assert(sub ell); // 5. 迭代器 for (auto ch : s2) { ch toupper(ch); } assert(s2 HELLO!); std::cout All tests passed! std::endl; }5.2 常见陷阱与排查实录在实现过程中我踩过不少坑这里分享几个典型的陷阱一忘记处理结尾的\0这是最常犯的错误之一。任何修改字符串长度_size的操作无论是push_back、append还是resize都必须确保在_str[_size]的位置正确放置\0。否则后续使用c_str()或传递给C库函数时会因找不到字符串结尾而引发错误。陷阱二自赋值处理在拷贝赋值运算符operator中如果不检查自赋值直接delete[] _str就会把自己需要的数据先释放掉导致后续拷贝出错。使用“拷贝-交换” idiom 是避免此问题最优雅的方式。陷阱三迭代器失效如前所述在循环中使用迭代器修改容器时如果中间插入了可能导致扩容的操作迭代器会失效。一个安全的模式是如果计划在循环中插入大量数据可以先reserve足够的空间。陷阱四new与delete[]不匹配用new[]分配数组就必须用delete[]释放。用delete释放数组是未定义行为。确保每个构造函数中new[]的分配都能在析构函数或reserve等函数中被正确地delete[]。调试技巧在构造函数、析构函数、reserve、赋值运算符等关键函数中加入调试输出打印_str地址、_size、_capacity可以清晰看到对象的生命周期和内存变化。使用ValgrindLinux/Mac或Dr. MemoryWindows等内存检测工具来运行你的测试程序它们能精准定位内存泄漏、越界访问、使用未初始化内存等问题。对于复杂的内存错误图形化调试器如VS、CLion、GDB的“监视”和“内存”视图非常有用可以实时查看指针指向的内容。亲手实现一个完整的String类就像完成一次C核心机制的深度游。它强迫你思考资源管理、对象生命周期、接口设计、异常安全和性能权衡。当你再使用std::string时你会对它的行为有更精准的预判写出更健壮高效的代码。这个练习的价值远不止于应付面试它是通向理解更复杂STL容器如vector,list的基石。

相关新闻

告别抢票焦虑:DamaiHelper全能抢票王帮你轻松搞定热门演出门票

告别抢票焦虑:DamaiHelper全能抢票王帮你轻松搞定热门演出门票

告别抢票焦虑:DamaiHelper全能抢票王帮你轻松搞定热门演出门票 【免费下载链接】damaihelper 支持大麦网,淘票票、缤玩岛等多个平台,演唱会演出抢票脚本 项目地址: https://gitcode.com/gh_mirrors/dam/damaihelper 还在为抢不到演唱会…

2026/10/17 14:54:45 阅读更多 →
科研与科技成果分类全解析:从论文专利到产业化应用

科研与科技成果分类全解析:从论文专利到产业化应用

1. 科研与科技成果的“家谱”:从实验室到应用的全景图 干了这么多年科研,也参与过不少成果转化和项目评审,我发现一个挺有意思的现象:很多刚入行的研究生,甚至一些工作了几年的科研人员,对“科技成果”的理…

2026/10/18 8:16:37 阅读更多 →
Scrapy+Redis构建亿级分布式爬虫架构实战

Scrapy+Redis构建亿级分布式爬虫架构实战

1. 项目概述:构建企业级分布式爬虫的核心要素 在大规模数据采集场景中,传统单机爬虫面临着性能瓶颈和可靠性问题。我经历过多个日采集量超过千万级的项目后,发现基于ScrapyRedis的分布式架构是性价比最高的解决方案之一。这种架构的核心在于将…

2026/10/14 18:12:28 阅读更多 →

最新新闻

工业数据对接新思路:边缘网关采集与HTTP接口拉取监测点全解析

工业数据对接新思路:边缘网关采集与HTTP接口拉取监测点全解析

做了这么多年工业数据采集和物联网平台对接,我最大的体会就是:设备侧的寄存器并不难,难的是把“采集到的数据”安全、稳定、按需地送到业务系统手里。很多工程师一看到“繁易平台直接对接监测点的数据”这个需求,第一反应就是上MQ…

2026/10/20 15:59:12 阅读更多 →
AI学习工具为何困在聊天框?从学习者建模到自适应练习的工程化突围

AI学习工具为何困在聊天框?从学习者建模到自适应练习的工程化突围

1. 一个让人又爱又恨的现状打开任何一个号称“AI 学习助手”的产品,十有八九你会看到一个熟悉的界面:一个输入框,下面配一个发送按钮,旁边可能还有几个预设的提示词模板。你问它一道数学题,它给你一段推导;…

2026/10/20 15:59:12 阅读更多 →
论文卡壳怎么破?一站式流程把定题到答辩串起来

论文卡壳怎么破?一站式流程把定题到答辩串起来

凌晨三点,宿舍走廊尽头的灯还亮着,你对着电脑屏幕上的摘要改了又删。论文卡壳这件事,我真的太熟了:不是不想写,是一打开文档就不知道手往哪儿放。后来我试过一套完全不同的打法——把写论文当成项目来运营,…

2026/10/20 15:59:12 阅读更多 →
Cursor从零搭建Python项目实战:AI优先编辑器的高效工作流

Cursor从零搭建Python项目实战:AI优先编辑器的高效工作流

第一次打开Cursor的时候,我其实是有点失望的。它看起来就是一个换了主题的VS Code,界面、快捷键、扩展市场都似曾相识。但真正上手十分钟后,我开始理解了:这个编辑器的核心根本不是“编辑”,而是“对话式开发”。如果你…

2026/10/20 15:59:12 阅读更多 →
Agent记忆系统:难在“记进去的那一刻”的写入端设计

Agent记忆系统:难在“记进去的那一刻”的写入端设计

1. 存储和检索早已不是瓶颈,真正的坎在写入端做 Agent 记忆系统的这大半年,我最大的感受是:大部分团队把精力花在了向量数据库选型、召回策略调优、混合检索权重配比这些事上,结果项目上线后最头疼的问题根本不是"存不进去&q…

2026/10/20 15:59:12 阅读更多 →
Python结合eBPF/XDP构建零信任网络安全框架实战

Python结合eBPF/XDP构建零信任网络安全框架实战

在讲这个项目之前,我先说一个实际场景:我接手过一套内部安全基座,最头疼的不是边界防火墙,而是内网主机和容器之间的东西向流量,基本处于“管不住”的状态。业务后端被攻破一台,攻击者拿它当跳板横向访问数…

2026/10/20 15:58:11 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →