【从C到C++的算法竞赛迁移指南】第二篇:动态数组与字符串完全攻略 —— 写给C程序员的全新世界

系列导航:

  1. 第一篇\] C++基础与竞赛优势

  2. 第三篇\] 映射与集合的终极形态

  3. 第五篇\] 现代语法糖精粹


一、动态数组:彻底告别malloc(手把手教学)

1.1 C程序员熟悉的痛苦场景

假设我们需要处理一个动态增长的整数数组,传统C代码是这样的:

c 复制代码
int* arr = NULL;    // 数组指针
int size = 0;       // 当前元素个数
int capacity = 0;   // 总容量

// 添加元素
void add_element(int val) {
    if(size >= capacity) {
        capacity = capacity ? capacity*2 : 1;
        arr = realloc(arr, capacity * sizeof(int)); // 可能失败!
    }
    arr[size++] = val;
}

1.2 C++的救星:vector容器

(1)基本概念
cpp 复制代码
vector<int> arr;  // 创建一个空数组
  • vector:动态数组类型(类似C的int[]但自动扩容)
  • <int>:模板参数(理解为"数组元素类型")
  • arr:对象名称(你可以用任何合法变量名)
(2)添加元素(对比教学)
c 复制代码
/* C版本 */
add_element(42); // 需要手动管理内存

/* C++版本 */
arr.push_back(42); // 自动处理内存
  • push_back():成员函数(类似C结构体中的函数指针)
  • 参数42会被自动添加到数组末尾
  • 内存自动翻倍扩容(无需手动realloc)
(3)访问元素(安全版vs快速版)
cpp 复制代码
// 安全访问(推荐新手使用)
int first = arr.at(0); // 越界会抛出异常

// 快速访问(与C数组相同行为)
int second = arr[1];  // 越界导致未定义行为

二、string:20个必会高效操作详解

1. 字符串构造

C++方式

cpp 复制代码
string s1;             // 空字符串(类似char s[1] = {0})
string s2("Hello");    // 从C字符串构造(自动计算长度)
string s3(5, 'A');     // "AAAAA"(C中需malloc+循环)

C对比:无需手动分配内存,自动处理'\0'结束符


2. 获取字符串长度

C++方式

cpp 复制代码
int len = s.size();    // O(1)时间复杂度

C对比strlen(s)需要O(n)遍历,C++内部维护长度值


3. 访问字符元素

安全访问

cpp 复制代码
char c = s.at(2);     // 越界抛出异常(建议调试使用)

快速访问

cpp 复制代码
char c = s[2];        // 与C数组相同(越界未定义行为)

4. 字符串拼接

C++方式

cpp 复制代码
string s = "Hello";
s += " World";       // 自动扩展内存(C需realloc)
s.append("!");       // 等同+=但可指定追加长度

C对比:无需计算目标缓冲区大小,自动管理内存


5. 字符串比较

C++方式

cpp 复制代码
if(s1 == s2) { ... }            // 直接比较内容
if(s1.compare(0,3,"App") == 0) // 比较前3字符是否"App"

C对比 :替代strcmp,更直观安全


6. 提取子串

C++方式

cpp 复制代码
string sub = s.substr(2, 5); // 从位置2取5字符(自动处理边界)

C对比 :替代strncpy+手动添加'\0'操作


7. 查找子串

基础查找

cpp 复制代码
size_t pos = s.find("World"); // 返回首次出现位置
if(pos != string::npos) { ... }

逆向查找

cpp 复制代码
pos = s.rfind('o');          // 从后向前查找字符

8. 替换子串

C++方式

cpp 复制代码
s.replace(6, 5, "CPP"); // 从位置6替换5字符为"CPP"

C对比:自动处理内存扩展,无需手动移动字符


9. 插入字符串
cpp 复制代码
s.insert(5, " dear"); // 在位置5插入字符串(自动后移字符)

10. 删除子串
cpp 复制代码
s.erase(5, 3);       // 从位置5删除3个字符(自动前移字符)

11. 首尾字符处理
cpp 复制代码
s.front() = 'h';      // 修改首字符(等同s[0])
s.pop_back();         // 删除末尾字符(比C少计算长度)

12. 清空字符串
cpp 复制代码
s.clear();           // 清空内容(内存保留)
s.shrink_to_fit();   // 释放多余内存(C中需realloc)

13. 判断空字符串
cpp 复制代码
if(s.empty()) { ... } // 比s.size() == 0更直观

14. 数据指针访问
cpp 复制代码
const char* cstr = s.c_str(); // 获取C风格字符串(只读)
char* buf = &s[0];            // 直接访问缓冲区(C++11起)

15. 流式处理
cpp 复制代码
stringstream ss("3.14 hello");
double d; string str;
ss >> d >> str;      // d=3.14, str="hello"(类似sscanf)

16. 数值转换
cpp 复制代码
int num = stoi("42");        // 字符串转int
double d = stod("3.14");     // 转double
string s = to_string(123);   // 数值转字符串

17. 大小写转换
cpp 复制代码
transform(s.begin(), s.end(), s.begin(), ::tolower);

C对比:无需逐个字符处理,一行代码完成


18. 删除空白字符
cpp 复制代码
s.erase(remove_if(s.begin(), s.end(), ::isspace), s.end());

效果:删除所有空白字符(空格、\t、\n等)


19. 字符串分割
cpp 复制代码
vector<string> split(const string& s, char delim) {
    vector<string> tokens;
    stringstream ss(s);
    string token;
    while(getline(ss, token, delim)) 
        if(!token.empty()) tokens.push_back(token);
    return tokens;
}

优势 :替代C中strtok函数,线程安全且不修改原字符串


20. 正则表达式(C++11)
cpp 复制代码
regex pattern(R"((\d+).(\d+))"); // 匹配数字(如3.14)
smatch match;
if(regex_search(s, match, pattern)) {
    string intPart = match[1];  // 获取第一个捕获组
    string decPart = match[2];
}

应用场景:复杂模式匹配,替代C中繁琐的手动解析


总结对比表(C vs C++ string)

操作需求 C语言实现 C++ string版本 优势总结
构造字符串 char[固定大小]或malloc 自动内存管理 无需预判长度
拼接字符串 strcat可能越界 +=自动扩展 安全便捷
获取长度 strlen遍历计数 size() O(1)获取 效率提升
子串替换 手动memmove+修改 replace自动处理 避免内存操作错误
字符串分割 strtok破坏原字符串 非破坏性split函数 保留原数据
模式匹配 手写解析循环 正则表达式 复杂模式易实现

通过掌握这20个核心操作,可替代C中90%的字符串处理代码,同时提升安全性和开发效率。每个操作都经过编译器高度优化,在算法竞赛中可放心使用。


三、邻接表示例深度解析

3.1 C语言版邻接表

c 复制代码
struct Node {
    int val;
    struct Node* next;
};

struct Node* graph[100]; // 100个节点的邻接表

// 添加边(1->5)
struct Node* newNode = malloc(sizeof(struct Node));
newNode->val = 5;
newNode->next = graph[1];
graph[1] = newNode;

3.2 C++ vector版

cpp 复制代码
// 创建100个节点的邻接表(每个节点对应一个vector)
vector<int> graph[100]; 

// 添加边(1连接5)
graph[1].push_back(5); 

// 解读:
// 1. graph是包含100个vector的数组
// 2. graph[1]访问第2个vector(下标从0开始)
// 3. push_back(5)向这个vector添加元素5

3.3 遍历邻居(对比教学)

c 复制代码
/* C遍历 */
struct Node* curr = graph[1];
while(curr != NULL) {
    printf("%d ", curr->val);
    curr = curr->next;
}

/* C++遍历 */
for(int neighbor : graph[1]) {  // 自动遍历每个元素
    cout << neighbor << " ";
}

四、避免Segmentation Fault的6大实践

4.1 典型错误场景

cpp 复制代码
vector<int> vec(3); // [0,0,0]
cout << vec[5];     // 越界访问(未定义行为)

string s;
cout << s[0];       // 访问空字符串(崩溃)

4.2 防御性编程技巧

  1. 始终检查空容器
cpp 复制代码
if(!vec.empty()) {
    // 安全访问vec[0]
}
  1. 使用at()替代[]
cpp 复制代码
try {
    cout << vec.at(5); // 抛出std::out_of_range
} catch(const exception& e) {
    cerr << e.what();
}
  1. 迭代器有效性检查
cpp 复制代码
auto it = vec.begin();
vec.push_back(42);  // 可能导致迭代器失效
// it可能失效,需重新获取
it = vec.begin();
  1. 预分配内存避免中间扩容
cpp 复制代码
vector<int> vec;
vec.reserve(1000);  // 预分配足够空间
  1. 智能指针管理资源(C++11)
cpp 复制代码
unique_ptr<int[]> arr(new int[100]); // 自动释放内存
  1. 范围检查工具(本地调试)
cpp 复制代码
#define _GLIBCXX_DEBUG  // GCC专用检查
vector<int> vec(3);
cout << vec[5]; // 立即触发错误提示

五、从C到C++的关键思维转变

5.1 变量即对象(理解.操作符)

cpp 复制代码
vector<int> arr;
arr.push_back(1); // arr是对象,调用其成员函数

/* 类似C的结构体操作 */
struct Vector {
    void (*push_back)(struct Vector*, int);
};
struct Vector arr;
arr.push_back(&arr, 1);

5.2 自动析构(内存自动释放)

cpp 复制代码
void func() {
    vector<int> arr(1000); // 分配内存
} // 函数结束时自动释放arr内存

/* 对比C语言 */
void func() {
    int* arr = malloc(1000 * sizeof(int));
    // ... 
    free(arr); // 必须手动释放!
}

六、编译与调试须知(重要!)

6.1 启用C++11标准

bash 复制代码
g++ -std=c++11 your_code.cpp  # 必须添加这个编译选项
  • 支持范围for循环、auto等现代特性

6.2 调试vector内存布局

cpp 复制代码
vector<int> arr = {1,2,3};
// 查看实际容量(非元素数量)
cout << "容量:" << arr.capacity() << endl;
// 输出:容量:4 (不同编译器可能有差异)

下篇预告

第三篇:映射与集合的终极形态

  • 如何用map替代C的手写二叉搜索树
  • unordered_map的哈希魔法
  • set实现自动去重排序
  • 选择数据结构的黄金法则

欢迎在评论区留下你在使用vector和string时遇到的问题~

相关推荐
2401_8582861117 分钟前
CD27.【C++ Dev】类和对象(18)友元和内部类
开发语言·c++·类和对象
(王子变青蛙)19 分钟前
C++初始
开发语言·c++·程序人生
莫有杯子的龙潭峡谷19 分钟前
4.15 代码随想录第四十四天打卡
c++·算法
LaoZhangGong12331 分钟前
MCU屏和RGB屏
经验分享·stm32·单片机·嵌入式硬件·fsmc
A懿轩A33 分钟前
2025年十六届蓝桥杯Python B组原题及代码解析
python·算法·蓝桥杯·idle·b组
灋✘逞_兇35 分钟前
快速幂+公共父节点
数据结构·c++·算法·leetcode
格里姆肖1 小时前
LVGL源码(7):渲染
c语言·stm32·单片机
code菜只因1 小时前
初始C语言(2)
c语言
跨境卫士TT1 小时前
东南亚斋月收官战:Shopee算法红利最后15天攻防指南
经验分享
双叶8361 小时前
(51单片机)LCD显示日期时间时钟(DS1302时钟模块教学)(LCD1602教程)
c语言·开发语言·数据库·单片机·嵌入式硬件·mongodb·51单片机