学数组和字符串这一章,我最大的感受是:终于不用再跟char数组死磕了!
C语言里,字符串就是char数组,各种越界、内存问题、strcpy/strcat/strlen…写起来费劲还容易出bug。到了C++,有了string类和vector容器,写代码突然就舒服了。
这篇就聊聊从C转C++时,数组和字符串这块的"幸福感提升"。
一、老熟人:C风格的数组和字符串
先快速过一遍C里就有的东西,C++基本都保留了:

  1. 一维数组、二维数组
    int arr[10]; // 一维数组
    int matrix[3][4]; // 二维数组
    定义、初始化、访问元素,和C一模一样。
  2. C风格字符串
    char str[] = “hello”; // 字符数组
    char *p = “world”; // 字符串常量指针
    还有那些熟悉的字符串函数:strlen、strcpy、strcat、strcmp、sprintf…
    回忆一下:刚学C的时候,谁没写过越界的字符串?谁没用strcpy写出过缓冲区溢出?C风格字符串就是这么"锻炼人"。
    二、C++的大杀器:string类
    如果说C++只能选一个我最喜欢的特性,string绝对能排进前三。
  3. 再也不用记一堆字符串函数了
    C风格字符串操作:
    #include
    char s1[100] = “hello”;
    char s2[100] = “world”;
    // 拼接
    strcat(s1, " ");
    strcat(s1, s2); // 还要担心缓冲区够不够大
    // 比较
    if (strcmp(s1, s2) == 0) { … } // 等于0才是相等,反直觉
    // 复制
    strcpy(s1, s2); // 还是要担心缓冲区
    C++ string操作:
    #include
    using namespace std;
    string s1 = “hello”;
    string s2 = “world”;
    // 拼接,直接+
    string s = s1 + " " + s2; // 不用担心溢出,自动管理内存
    // 比较,直接==
    if (s1 == s2) { … } // 直观!
    if (s1 > s2) { … } // 大小比较也自然
    // 赋值,直接=
    s1 = s2; // 简单直接
    对比一下,体验差太多了!
  4. string的常用操作
    表格
    操作 用法 说明
    获取长度 s.size() 或 s.length() 返回字符个数
    判断空 s.empty() 空返回true
    清空 s.clear() 清空内容
    追加 s += “abc” 或 s.append(“abc”) 在末尾追加
    插入 s.insert(pos, “abc”) 在pos位置插入
    删除 s.erase(pos, len) 删除从pos开始的len个字符
    取子串 s.substr(pos, len) 取子字符串
    查找 s.find(“abc”) 查找子串,返回位置
    替换 s.replace(pos, len, “abc”) 替换
    还有很多方法,用的时候查文档就行。
  5. string和C风格字符串的转换
    有时候需要用C风格的字符串(比如调用老的C函数),string提供了c_str()方法:
    string s = “hello”;
    const char p = s.c_str(); // 得到const char指针
    // 反过来也可以
    string s2 = p; // char*直接转string
    注意:c_str() 返回的指针是 const 的,不要去修改它指向的内容。而且这个指针可能会在string被修改后失效,别存着一直用。
  6. string的"坑"
    虽然string很好用,但也有一些需要注意的地方:
    坑1:用[]访问越界不会检查
    string s = “hello”;
    s[10] = ‘a’; // 越界了!不会报错,但行为未定义
    想用安全的访问,用 s.at(10),越界会抛异常。
    坑2:字符串字面量不是string
    “hello” + “world”; // 错误!两个char不能相加
    因为"hello"是C风格字符串(const char
    ),不是string对象,不能直接+。想拼接得转成string:
    string(“hello”) + “world”; // 可以
    三、从C到C++的思维转变
  7. 从"手动管理"到"自动管理"
    C里数组和字符串的内存都要自己管,C++里string和vector帮你管了。不用再担心缓冲区溢出、内存泄漏这些问题。
  8. 从"字符数组"到"字符串对象"
    C里字符串就是个特殊的char数组,C++里string是个完整的对象,有自己的方法和行为。写代码的思路从"操作内存"变成了"操作对象"。
  9. 从"数组=指针"到"数组=容器"
    C里数组和指针关系暧昧,很容易搞混。C++里vector和string都是独立的容器对象,有自己的接口,不用和指针纠缠。
    四、我的踩坑记录
    坑1:string的[]不检查越界
    前面说过了,用[]访问越界是未定义行为,不会报错。要安全访问用at()。
    坑2:C风格字符串和string混用
    string s = “hello”;
    if (s == “world”) { … } // 没问题,"world"会自动转string
    if (“hello” == “world”) { … } // 有问题!这是在比较两个const char*指针,不是比较内容
    两个C风格字符串比较要用strcmp,不能直接用==。
    四、一些实用建议
  10. 尽量用string,少用char数组
    除非有特殊理由(比如性能极致优化、和C库交互),否则优先用string。安全、方便、功能强。
  11. 范围for遍历真香
    能用范围for就别写下标,简洁又不容易写错。
    写在最后
    数组和字符串这一章,给我的感觉是:C++终于把C里最让人头疼的部分给包好了。
    C的字符串和动态数组,不是不能用,而是用起来太"糙"了——啥都要自己来,稍不注意就出问题。C++的string和vector把这些底层细节都藏起来了,让你能专注于逻辑本身,而不是跟内存较劲。
    当然,这不是说C风格的数组和字符串就不用学了——很多老代码、C库还是要用的。但平时写C++代码的时候,能用上string和vector就尽量用,效率和安全性都高很多。

更多推荐