一、C++字符串簡介
在C++中,字符串的處理方式主要有兩種:字符數組(C風格字符串)和std::string類。雖然字符數組是C語言遺留的底層實現方式,但現代C++更推薦使用std::string
類,其封裝了復雜的操作邏輯,提供了更高的安全性和便利性。本文將深入解析C++字符串的原理、常用操作,并結合力扣算法題進行實戰演練。
二、C++字符串的原理
1. 字符數組(C風格字符串)
- 原理:字符數組是以
\0
結尾的字符序列,通過手動管理內存實現字符串操作。 - 特點:
- 需預分配固定大小,容易溢出。
- 操作依賴標準庫函數(如
strcpy
、strlen
等),需手動處理邊界問題。 - 適合對內存控制要求極高的底層場景。
char str[10] = "hello"; // 存儲"hello\0"
2. std::string類
- 原理:
std::string
是C++標準庫提供的動態字符串類,內部通過動態內存管理實現自動擴容和縮容。 - 特點:
- 自動管理內存,無需手動分配/釋放。
- 提供豐富的成員函數(如拼接、查找、替換等)。
- 支持STL容器和算法,兼容性高。
- 通過移動語義(C++11)優化臨時對象的資源轉移效率。
#include <string>
std::string s = "hello";
s += " world"; // 自動擴容
三、C++字符串的常用操作
1. 賦值與初始化
std::string s1 = "hello"; // 直接賦值
std::string s2(5, 'a'); // 構造5個'a'組成的字符串
std::string s3(s1); // 拷貝構造
std::string s4 = s1 + s2; // 拼接賦值
2. 字符串拼接
std::string s = "Hello";
s += " C++"; // 運算符重載
s.append(" world"); // 成員函數
3. 查找與替換
std::string s = "Hello World";
size_t pos = s.find("World"); // 查找子串位置
if (pos != std::string::npos) {s.replace(pos, 5, "C++"); // 替換為"C++"
}
4. 子串提取
std::string s = "Hello C++";
std::string sub = s.substr(6, 4); // 提取從索引6開始的4個字符:"C++"
5. 字符串比較
std::string a = "apple", b = "banana";
if (a < b) { // 字典序比較std::cout << "a is smaller";
}
6. 長度與空判斷
std::string s = "hello";
std::cout << s.size(); // 輸出長度(5)
std::cout << s.empty(); // 判斷是否為空(0)
7. 邊界安全訪問
std::string s = "example";
char ch1 = s[2]; // 不檢查邊界
char ch2 = s.at(2); // 檢查邊界,越界拋出異常
四、力扣算法實戰:字符串應用場景
1.?回文串驗證(LeetCode 125)
題目:驗證字符串是否為回文串(忽略非字母數字字符,不區分大小寫)。
思路:雙指針法,從兩端向中心遍歷,逐字符比較。
#include <string>
#include <cctype>
using namespace std;bool isPalindrome(string s) {int left = 0, right = s.size() - 1;while (left < right) {while (left < right && !isalnum(s[left])) left++;while (left < right && !isalnum(s[right])) right--;if (tolower(s[left]) != tolower(s[right])) return false;left++, right--;}return true;
}
2.?最長公共前綴(LeetCode 14)
題目:找出多個字符串的最長公共前綴。
思路:縱向掃描,逐字符比較所有字符串的相同位置。
#include <string>
#include <vector>
using namespace std;string longestCommonPrefix(vector<string>& strs) {if (strs.empty()) return "";for (int i = 0; i < strs[0].size(); ++i) {char c = strs[0][i];for (int j = 1; j < strs.size(); ++j) {if (i == strs[j].size() || strs[j][i] != c) {return strs[0].substr(0, i);}}}return strs[0];
}
3.?字符串轉換整數(atoi)(LeetCode 8)
題目:將字符串轉換為整數,處理空格、符號和溢出。
思路:狀態機處理輸入,逐字符解析。
#include <string>
#include <climits>
using namespace std;int myAtoi(string s) {int index = 0, sign = 1, result = 0;while (s[index] == ' ') index++;if (s[index] == '-' || s[index] == '+') {sign = (s[index++] == '-') ? -1 : 1;}while (index < s.size() && isdigit(s[index])) {int digit = s[index++] - '0';if (result > INT_MAX / 10 || (result == INT_MAX / 10 && digit > INT_MAX % 10)) {return (sign == 1) ? INT_MAX : INT_MIN;}result = result * 10 + digit;}return sign * result;
}
五、知識點總結
1.?std::string vs 字符數組
特性 | std::string | 字符數組(char[] ) |
---|---|---|
內存管理 | 自動擴容/縮容 | 靜態分配,需手動管理 |
操作便捷性 | 成員函數豐富(如find 、substr 等) | 依賴標準庫函數(如strcpy 等) |
安全性 | 邊界檢查(at() )、異常處理 | 容易溢出,需手動添加\0 |
性能 | 高(現代編譯器優化) | 更低(頻繁內存拷貝) |
推薦使用場景 | 通用開發、STL兼容 | 嵌入式系統、協議解析等底層場景 |
2.?設計建議
- 優先使用
std::string
:現代C++開發中,std::string
是首選方式,其封裝了復雜邏輯,減少內存泄漏風險。 - 避免字符數組:除非對性能或內存布局有嚴格要求,否則不建議使用字符數組。
- 利用移動語義(C++11):臨時對象的資源轉移通過
std::move
優化效率。 - 邊界檢查:使用
at()
代替[]
避免越界訪問。