【C++STL】告别 C 字符串噩梦!C++ string 类从入门到精通,含实战避坑指南

【C++STL】告别 C 字符串噩梦!C++ string 类从入门到精通,含实战避坑指南
在这里插入图片描述
🔥个人主页:爱和冰阔乐
📚专栏传送门:《数据结构与算法》C++
🐶学习方向:C++方向学习爱好者
⭐人生格言:得知坦然 ,失之淡然
在这里插入图片描述


文章目录


前言:认识 STL——C++ 标准库的基石

在深入探讨string之前,我们有必要先了解它所属的 “大家庭”——STL(Standard Template Library,标准模板库)。STL 是 C++ 标准库的核心组成部分,由 Alexander Stepanov 于 1994 年提出,它以泛型编程为思想,将数据结构(容器)与算法分离,通过迭代器连接,为开发者提供了一套高效、可复用的组件。

STL主要包含六大组件:

在这里插入图片描述

容器(Containers): 封装数据结构的类模板(如vector、list、map、string等),用于存储和管理数据

算法(Algorithms): 实现常用操作的函数模板(如sort、find、copy等),可作用于容器中的元素

迭代器(Iterators): 连接容器与算法的 “桥梁”,提供类似指针的接口,使算法能统一访问不同容器

仿函数(Functors): 重载operator()的类 / 结构体,可作为算法的策略参数(如自定义排序规则)

适配器(Adapters): 修改容器、迭代器或仿函数的接口(如stack、queue是deque的适配器)

分配器(Allocators): 负责容器的内存管理,隐藏底层内存分配细节

STL 的优势在于高复用性(一套代码适配多种数据类型)、高性能(底层经严格优化)和标准化(跨平台一致行为)。而string作为 STL 中专门处理字符串的容器,完美体现了 STL 的设计思想 —— 它封装了字符序列的存储与操作,支持迭代器访问,可与 STL 算法(如reverse、find)无缝配合,是我们学习 STL 的绝佳起点。

在正常笔试中STL也会经常被考到,如:把二叉树打印成多行用两个栈实现队列

网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。

因此我们便可以看出STL的重要性了!!!


一、先搞懂:为什么要抛弃 C 字符串用 string?

在学string之前,我们得先明白:C 语言的字符串到底 “烂” 在哪里?

1.C字符串的三大痛点

C 语言里,字符串是’\0’结尾的字符数组,搭配str系列库函数(strcpy、strcat、strlen)使用,但问题一大堆:

数据与操作分离: 字符串是数组,库函数是独立的,比如strcpy(s1, s2)需要传两个参数,不符合 “对象 = 数据 + 方法” 的 OOP 思想

内存全靠手动管: 要自己malloc分配空间、free释放,忘了free就内存泄漏,分配小了就越界

越界风险极高: 比如strcat(s, “hello”),如果s的缓冲区不够大,直接触发未定义行为(程序崩溃是轻的)

举个真实的踩坑案例:

// C语言字符串的坑char buf[5];strcpy(buf,"hello");// 缓冲区只有5字节,"hello"占6字节(含'\0'),越界!

这种问题在string里根本不会出现 —— 它会自动扩容,不用你操心内存!

C++ 的string类完美解决了这些问题:它封装了字符串的存储与操作,自动管理内存,提供了丰富的成员函数,且在 OJ 和实际开发中几乎完全替代了 C 语言字符串。

2.现实需求

1.OJ 刷题: LeetCode、牛客网的字符串题,99% 都是以string类给出的

2.实际开发: 没人会放着string不用去折腾 C 字符串,string的接口更简洁(比如拼接直接用+=,求长度用size()),能大幅减少 bug。

二、C++11 小助手:auto 与范围 for,用 string 更顺手

在正式学string接口前,先掌握两个 C++11 语法糖 ——auto和范围 for,它们是使用string的 “神器”,是提升编码效率的利器

1 前置知识:C++11 的 auto 与范围 for
string的遍历和操作常依赖这两个特性,先快速上手:

1. auto:让编译器帮你推类型

在这里补充2个C++11的小语法,方便我们后面的学习

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

auto不能作为函数的参数,可以做返回值,但是建议谨慎使用auto不能直接用来声明数组

auto:auto在 C++11 里的新含义是类型推导:声明变量时不用写具体类型,编译器会根据初始化值自动判断,尤其适合复杂类型(如map迭代器、string迭代器)

代码举例1:

#include<iostream>usingnamespace std;intfunc1(){return10;}// 不能做参数voidfunc2(auto a){}// 可以做返回值,但是建议谨慎使用autofunc3(){return3;}intmain(){int a =10;auto b = a;auto c ='a';auto d =func1();// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项auto e; cout <<typeid(b).name()<< endl; cout <<typeid(c).name()<< endl; cout <<typeid(d).name()<< endl;int x =10;auto y =&x;auto* z =&x;auto& m = x; cout <<typeid(x).name()<< endl; cout <<typeid(y).name()<< endl; cout <<typeid(z).name()<< endl;auto aa =1, bb =2;// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型auto cc =3, dd =4.0;// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型auto array[]={4,5,6};return0;}

代码举例2:

#include<iostream>#include<string>#include<map>usingnamespace std;intmain(){// 1. 基础类型推导 string s ="hello";auto len = s.size();// 推导为size_tauto ch = s[0];// 推导为char// 2. 指针/引用推导:指针用auto/*均可,引用必须加&int a =10;auto* p =&a;// 指针类型auto& ref = a;// 引用类型(必须加&)// 3. 实战场景:简化迭代器 map<string, string> dict ={{"apple","苹果"},{"orange","橙子"}};// 无需写:map<string, string>::iterator it = dict.begin()auto it = dict.begin();while(it != dict.end()){ cout << it->first <<":"<< it->second << endl;++it;}return0;}

避坑点:

同一行声明多个变量时,类型必须一致(auto a=1, b=2.0报错)

不能作为函数参数(void func(auto a)报错)

不能直接声明数组(auto arr[] = {1,2,3}报错)

实战场景:遍历 map(没 auto 会疯!)
没有 auto 时,迭代器声明长到离谱;有了 auto,一行搞定:

#include<map>#include<string>usingnamespace std;intmain(){ map<string, string> dict ={{"apple","苹果"},{"orange","橙子"}};// 没有auto:冗长! map<string, string>::iterator it1 = dict.begin();// 有auto:简洁!auto it2 = dict.begin();while(it2 != dict.end()){ cout << it2->first <<":"<< it2->second << endl;++it2;}return0;}

核心用法与注意事项的总结:

2.范围 for:遍历 string / 数组

C++11 的范围 for专门解决 “遍历集合” 的重复工作 —— 不用写循环条件、不用算索引,自动遍历每个元素。

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。范围for可以作用到数组和容器对象上进行遍历范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

语法与用法:

// 语法:for(迭代变量 : 被遍历的范围)for(auto 变量 : 数组/string/容器){// 操作变量}

对比 C++98 和 C++11 的遍历方式,差距一目了然:

#include<string>#include<iostream>usingnamespace std;intmain(){ string str ="hello string";// 1. C++98遍历:算长度、写索引,麻烦!for(int i =0; i < str.size();++i){ cout << str[i]<<" ";} cout << endl;// 2. C++11范围for:简洁!for(auto ch : str){// auto自动推成char cout << ch <<" ";} cout << endl;// 3. 要修改元素?加&(引用)for(auto& ch : str){ ch =toupper(ch);// 转大写} cout << str;// 输出HELLO STRINGreturn0;}

范围 for 底层是迭代器实现的(编译时会被替换为begin()/end()的循环)所以支持所有 STL 容器(vector、map等)和数组、string。


三、string 类核心接口全解析(必学!)

string类的接口很多,但实际开发中常用的就那么几个。我们按 “构造→容量→访问→修改” 的顺序梳理,附代码示例和避坑点。如若想了解更多接口,小手点我可以查看哦

1. string 对象的构造(3 种常用)

构造string就像 “创建字符串”,最常用的 3 种方式如下表,直接看代码更直观:

在这里插入图片描述
#include<string>usingnamespace std;voidTestString(){ string s1;// 空串,s1.size()=0 string s2("hello");// 用C字符串"hello"初始化 string s3(s2);// 拷贝构造,s3和s2都是"hello"}

2.容量操作

“容量” 指 string 管理的内存空间,这几个接口是避免内存频繁扩容的关键,必须吃透!

在这里插入图片描述

注意:

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接
    口保持一致,一般情况下基本都是用size()
  2. clear()只是将string中有效字符清空,不改变底层空间大小
  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
  4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参
    数小于string的底层空间总大小时,reserver不会改变容量大小。

实战示例:reserve 提升效率

如果要往 string 里插入 1000 个字符,不预分配空间会频繁扩容(每次扩容复制数据,耗时);用reserve预分配后效率翻倍:

#include<string>#include<chrono>// 计时usingnamespace std;intmain(){ string s1, s2; s2.reserve(1000);// 预分配1000个空间// 统计插入1000个字符的时间auto start1 = chrono::high_resolution_clock::now();for(int i =0; i <1000;++i) s1 +='a';auto end1 = chrono::high_resolution_clock::now();auto start2 = chrono::high_resolution_clock::now();for(int i =0; i <1000;++i) s2 +='a';auto end2 = chrono::high_resolution_clock::now();// s2比s1快很多(扩容次数少) cout <<"s1耗时:"<< chrono::duration_cast<chrono::nanoseconds>(end1-start1).count()<<"ns\n"; cout <<"s2耗时:"<< chrono::duration_cast<chrono::nanoseconds>(end2-start2).count()<<"ns\n";return0;}

结果如下:

在这里插入图片描述

实战示例:容量操作的正确姿势

voidTestCapacity(){ string s;// 1. 预留空间:避免频繁扩容(比如已知要存100个字符) s.reserve(100); cout <<"reserve后capacity: "<< s.capacity()<< endl;// 至少100// 2. 调整有效长度 s.resize(10,'a');// 有效字符变为10个'a',size=10 cout <<"resize后size: "<< s.size()<< endl;// 3. 清空内容 s.clear(); cout <<"clear后size: "<< s.size()<< endl;// 0 cout <<"clear后capacity: "<< s.capacity()<< endl;// 仍为100(未释放)}

避坑点:

1.reserve(n):若 n≤当前capacity,则不做任何操作

2.resize(n):若未指定填充字符,默认用\0填充(而非空格)

3.访问与遍历

访问 string 的单个字符,首选operator[](像数组一样方便),配合范围 for 或迭代器遍历更灵活。

在这里插入图片描述
#include<string>#include<iostream>usingnamespace std;intmain(){ string s ="hello";// 1. operator[]访问(像数组一样) cout << s[0]<< endl;// 输出'h' s[1]='E';// 修改为'hEllo'// 2. 迭代器遍历auto it = s.begin();while(it != s.end()){ cout <<*it <<" ";// 输出h E l l o++it;}return0;}

4.修改操作

string 的修改接口很多,但 +=、find、substr 是实际开发中用得最多的,比如拼接字符串、查找子串、截取子串

在这里插入图片描述


实战场景:截取文件后缀名
代码实现:

#include<string>#include<iostream>usingnamespace std;intmain(){ string filename ="test.txt"; size_t pos = filename.find('.');// 找'.'的位置(返回4)if(pos != string::npos){// 确保找到 string suffix = filename.substr(pos);// 从pos截取到末尾,得到".txt" cout << suffix << endl;}return0;}

小贴士:string::npos是 string 的静态常量,表示 “未找到”(值为-1,但类型是size_t,所以不要用int接收返回值)

实战示例:字符串截取与查找

voidTestModify(){ string s ="hello world";// 1. 查找并截取 size_t pos = s.find(' ');// 找到空格位置(5) string s1 = s.substr(0, pos);// 截取"hello" string s2 = s.substr(pos+1);// 截取"world"(n省略则取到末尾)// 2. 追加 s1 +=" C++";// s1变为"hello C++" s1.push_back('!');// s1变为"hello C++!"// 3. 转为C字符串constchar* cs = s1.c_str(); cout << cs << endl;// 输出:hello C++!}

效率提示:追加字符时,+=比push_back更灵活(支持字符串),比append更简洁,优先使用+=

5. 非成员函数(4 个常用)

string的非成员函数主要用于输入输出和比较:

operator<>:输出 / 输入字符串(>>遇空格终止)

getline(cin, s):读取一行字符串(包含空格,解决>>的缺陷)

关系运算符(==、!=、<等):直接比较字符串内容(按 ASCII 码排序)

四、揭秘底层:vs 和 g++ 的 string 实现居然不一样!

很多人只会用string,但不知道它底层怎么存的 —— 不同编译器的实现差异很大,这也是面试常考点!我们以32 位平台为例,看看 vs 和 g++ 的区别。

1.vs 的 “小字符串优化”(SSO):兼顾效率与空间

vs 下的string占28 字节,核心设计是 “小字符串用栈,大字符串用堆”:

1.当字符串长度 <16 时:用内部固定的 16 字节数组(栈空间)存放,不用分配堆内存,效率极高

2.当字符串长度≥16时:从堆上分配空间,数组存堆地址

内部结构拆解(28 字节):
联合体(16 字节):存小字符串数组或堆指针
size_t(4 字节):有效字符长度
size_t(4 字节):堆空间总容量
指针(4 字节):其他辅助功能

在这里插入图片描述

为什么这么设计? 因为大多数场景下的字符串都很短(比如文件名、用户名),用栈空间避免了堆分配的开销,效率翻倍

2.g++ 的 “写时拷贝”(COW):共享内存省空间

g++ 下的string更精简,只占4 字节—— 内部就是一个指针,指向堆上的一块 “控制块”,控制块包含 3 个信息:

  1. 字符串有效长度
  2. 堆空间总容量
  3. 引用计数(记录有多少个 string 共享这块内存)。

“写时拷贝” 的逻辑:
当用string s2(s1)拷贝时,不复制内存,只给引用计数 + 1(s1 和 s2 共享同一块内存);
当修改 s2 时(比如s2[0] = ‘a’),才会真正复制内存(此时引用计数 - 1,s2 单独拥有新内存)。

优缺点:读取时高效(共享内存),但写入时需要拷贝,且多线程下有线程安全问题(现在 g++ 的新版本已逐渐弃用 COW)

五、避坑指南:浅拷贝的 “致命陷阱” 与深拷贝实现

面试中,面试官最爱问:“自己实现一个 string 类,要注意什么?”—— 核心就是避开浅拷贝的坑!

1.浅拷贝:为什么会崩溃?——共用资源的灾难

如果我们天真地实现一个 “极简 string”,不写自己的拷贝构造和赋值运算符,编译器会生成默认浅拷贝——仅拷贝指针值(而非指针指向的内容),导致多个对象共用同一块堆空间:

// 错误示范:极简string(浅拷贝问题)classString{public:// 构造String(constchar* str =""){ _str =newchar[strlen(str)+1];strcpy(_str, str);}// 析构~String(){delete[] _str; _str =nullptr;}// 未写拷贝构造和赋值运算符(编译器生成默认的)private:char* _str;};// 测试:崩溃!voidTest(){ String s1("hello"); String s2(s1);// 浅拷贝:s1._str和s2._str指向同一块内存}// 函数结束时:先析构s2(释放内存),再析构s1(释放已释放的内存→崩溃)

问题根源:浅拷贝只复制指针值,导致多个对象共享同一块内存,销毁时 “二次释放”。

这里我们来说下浅拷贝的两个大问题:
1.会导致同一块空间析构两次
2.修改一个便会导致另外一个被修改

形象比喻:浅拷贝像「两个孩子共用一个玩具」,一个弄坏了另一个没法玩

2.深拷贝:正确的实现方式(三种写法)

深拷贝的核心是:每个对象独立分配内存,不共享资源,需显式实现拷贝构造函数和赋值运算符重载,有「传统版」和「现代版」两种写法

写法 1:传统版(先分配,再拷贝,最后释放旧内存)

classString{public:// 构造String(constchar* str =""){if(str ==nullptr) str =""; _str =newchar[strlen(str)+1];strcpy(_str, str);}// 拷贝构造(深拷贝)String(const String& s){ _str =newchar[strlen(s._str)+1];// 独立分配内存strcpy(_str, s._str);// 拷贝内容}// 赋值运算符(深拷贝:避免自赋值,先拷贝再释放) String&operator=(const String& s){if(this!=&s){// 避免自赋值(s = s)char* tmp =newchar[strlen(s._str)+1];strcpy(tmp, s._str);delete[] _str;// 释放旧内存 _str = tmp;}return*this;}// 析构~String(){delete[] _str; _str =nullptr;}private:char* _str;};

写法 2:现代版(利用临时对象,更简洁安全)

现代版借助 “临时对象的析构” 来简化代码,不用手动管理旧内存:

classString{public:// 构造(同上)String(constchar* str =""){if(str ==nullptr) str =""; _str =newchar[strlen(str)+1];strcpy(_str, str);}// 拷贝构造(现代版:用临时对象swap)String(const String& s):_str(nullptr){ String tmp(s._str);// 用s的内容构造临时对象tmpswap(_str, tmp._str);// 交换tmp和当前对象的_str}// 临时对象tmp析构时,释放原来的_nullptr(无害)// 赋值运算符(现代版:参数传值,自动生成临时对象) String&operator=(String s){// s是实参的拷贝(临时对象)swap(_str, s._str);// 交换当前对象和s的_strreturn*this;}// s析构时,释放原来的旧内存// 析构(同上)~String(){delete[] _str; _str =nullptr;}private:char* _str;};

简单来说,现代版就是资本家不需要手动去实现,而是雇佣了牛马去帮他实现,最后成功被资本家窃取

总结:现代版更简洁,避免了 “先释放旧内存再分配新内存” 的中间状态,从根源上防止内存泄漏

写实拷贝

在这里插入图片描述


写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该
资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有
其他对象在使用该资源。

点击我带你了解写实拷贝写实拷贝在读取时的缺陷

六、模拟实现string

模拟实现string的常见接口,点击它即可进入string的模拟实现代码仓库

总结

string看似简单,实则是 C++ 内存管理、OOP 思想、STL 设计的浓缩体现。掌握它的关键在于:

  1. 基础优先:熟练使用auto、范围 for,牢记构造、容量、修改的核心接口
  2. 深入底层:理解 VS 的 SSO 和 G++ 的 COW 差异,搞懂浅拷贝的危害与深拷贝的实现
  3. 多练实战:通过 OJ 题巩固接口使用,模拟实现加深对资源管理的理解
  4. 面试聚焦:重点准备「深拷贝的两种写法」「SSO 原理」「string 与 C 字符串的转换」

最后如果觉得这篇文章有用,欢迎点赞收藏~ 有任何问题,评论区一起讨论!

Read more

“现在的AI就像1880年的笨重工厂!”微软CSO斯坦福泼冷水:别急着造神

“现在的AI就像1880年的笨重工厂!”微软CSO斯坦福泼冷水:别急着造神

大模型仍未对上商业的齿轮? 编译 | 王启隆 来源 | youtu.be/aWqfH0aSGKI 出品丨AI 科技大本营(ID:rgznai100) 现在的硅谷,空气里都飘着一股“再不上车就晚了”的焦躁感。 最近 OpenClaw 风头正旺,强势登顶 GitHub,终结了 React 神话,许多人更是觉得“AI 自己干活赚钱”的日子就在明天了。 特别是在斯坦福商学院(GSB)这种地方,台下坐着的都是成天琢磨怎么用下一个技术风口搞个独角兽出来的狠人。 微软的首席科学官(CSO)Eric Horvitz 被请到了这个几乎全美最想用 AI 变现的礼堂里。作为从上世纪 80 年代就开始搞 AI 的绝对老炮、也是微软技术底座的“扫地僧”,这位老哥并没有顺着台下的胃口,去吹捧下个月大模型又要颠覆什么行业,而是兜头给大家浇了一盆带点学术味的冷水。 他讲了一个挺有画面感的比喻:大家都在聊

By Ne0inhk
Godot被AI代码“围攻”!维护者崩溃发声:“不知道还能坚持多久”

Godot被AI代码“围攻”!维护者崩溃发声:“不知道还能坚持多久”

整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 当大模型能在几秒钟内生成一段“看起来像那么回事”的补丁时,开源社区却开始付出另一种代价。 最近,开源游戏引擎 Godot 的核心维护团队公开吐槽:他们正被大量“AI 生成的低质量代码”淹没。那些代码往往结构完整、注释齐全、描述洋洋洒洒,但真正的问题是——提交者可能并不理解自己交上来的内容。 这件事,并不是简单的“有人偷懒用 AI 写代码”。它正在触及开源协作最核心的东西:信任。 一场悄无声息的“AI 洪水” 事情的导火索来自一条 Bluesky 讨论帖。 Godot 主要维护者之一、同时也是 Godot 商业支持公司 W4 Games 联合创始人的 Rémi Verschelde 表示,所谓的“AI slop”

By Ne0inhk
诺奖得主辛顿最新访谈:1 万个 AI 可以瞬间共享同一份“灵魂”,这就是为什么人类注定被超越

诺奖得主辛顿最新访谈:1 万个 AI 可以瞬间共享同一份“灵魂”,这就是为什么人类注定被超越

当宇宙级的“嘴炮”遇到降维打击。 编译 | 王启隆 来源 | youtu.be/l6ZcFa8pybE 出品丨AI 科技大本营(ID:rgznai100) 打开最新一期知名播客 StarTalk 的 YouTube 评论区,最高赞的一条留言是这样写的: “我长这么大,第一次看到尼尔·德葛司·泰森(Neil deGrasse Tyson)在一档节目里几乎全程闭嘴,像个手足无措的小学生一样乖乖听讲。” 作为全美最知名的天体物理学家,泰森平时的画风是充满激情、喋喋不休、用宇宙的宏大来震撼嘉宾。但这一次,坐在他对面的那位满头银发、带着温和英音的英国老人,仅仅用最平淡的语气,就让整个演播室陷入了数次令人窒息的沉默。 这位老人是 Geoffrey Hinton。深度学习三巨头之一,2024 年诺贝尔物理学奖得主,被公认为“AI 教父”。 对经常阅读 Hinton 演讲的我来说,这也是比较新奇的一幕—

By Ne0inhk
48小时“烧光”56万!三人创业团队濒临破产,仅因Gemini API密钥被盗:“AI账单远超我们的银行余额”

48小时“烧光”56万!三人创业团队濒临破产,仅因Gemini API密钥被盗:“AI账单远超我们的银行余额”

整理 | 苏宓 出品 | ZEEKLOG(ID:ZEEKLOGnews) 「仅过了 48 小时,一笔 8.2 万美元的天价费用凭空出现,较这家小型初创公司的正常月费暴涨近 46000%。」 这不是假设的虚幻故事,而是一家墨西哥初创公司正在经历的真实危机。 近日,一位名为 RatonVaquero 的开发者在 Reddit 发帖求助称,由于他的 Gemini API 密钥被盗用,原本每月仅约 180 美元(约 1242 元)的费用,在短短 48 小时内暴涨到 82,314.44 美元(约 56.8 万元)。对于这家只有三名开发者的小型创业团队来说,这笔突如其来的账单,几乎等同于灭顶之灾。 “我现在整个人都处在震惊和恐慌之中。”RatonVaquero

By Ne0inhk