从零到一学习C++(基础篇) 作者:羡鱼肘子
温馨提示1:本篇是记录我的学习经历,会有不少片面的认知,万分期待您的指正。
温馨提示2:本篇会尽量用更加通俗的语言介绍c++的基础,用通俗的语言去解释术语。
温馨提示3:看本篇前可以先了解前篇的内容,知识体系会更加完整哦。
从零到一学习c++(基础篇--筑基期五-数组、指针)-CSDN博客
标准库类型:string
C++中的标准库类型
std::string
,它是C++中用于处理字符串的类,比C风格的字符数组更强大、更安全。
一、std::string
是什么?
std::string
是 C++ 标准库提供的一个“字符串类”,专门用来处理文本数据。你可以把它想象成一个“智能的字符数组”,它不仅能存储一串字符(比如 "Hello"
),还能帮你完成很多常见的字符串操作,比如拼接、查找、替换等。
术语解释
-
字符串:一串字符的集合,比如
"Hello World"
。 -
字符数组:C 语言中用来表示字符串的方式,比如
char str[] = "Hello"
。 -
类:C++ 中的一种数据类型,可以包含数据(成员变量)和操作数据的方法(成员函数)。
-
动态内存管理:
std::string
会自动分配和释放内存,不需要手动管理。
二、std::string
的基本用法
1. 定义和初始化
你可以用多种方式创建一个 std::string
对象,比如直接赋值、用构造函数初始化,或者用重复字符初始化。
代码示例
#include <string> // 必须包含这个头文件std::string str1; // 默认初始化,空字符串
std::string str2 = "Hello"; // 用C风格字符串初始化
std::string str3("World"); // 用构造函数初始化
std::string str4(5, 'A'); // 初始化包含5个'A'的字符串
std::string str5 = str2; // 用另一个string对象初始化
术语解释
-
默认初始化:创建一个空字符串。
-
C风格字符串:以
\0
结尾的字符数组,比如"Hello"
。 -
构造函数:类的特殊函数,用于初始化对象。
2. 访问字符
你可以像访问数组一样访问字符串中的某个字符,比如 str[1]
表示字符串中的第二个字符。
代码示例
std::string str = "Hello";
char ch1 = str[1]; // ch1 = 'e'(下标从0开始)
char ch2 = str.at(1); // ch2 = 'e'(推荐用at(),因为它会检查越界)
术语解释
-
下标运算符
[]
:直接访问字符,不检查越界。 -
at()
方法:访问字符时会检查下标是否越界,如果越界会抛出异常。(推荐哦)
3. 获取字符串长度
用 size()
或 length()
方法可以获取字符串的长度,比如 "Hello"
的长度是 5。
代码示例
std::string str = "Hello";
int len = str.size(); // len = 5
int len2 = str.length(); // len2 = 5
术语解释
-
长度:字符串中字符的个数。
温馨小贴士:
string 存的字符串末尾有'\0'吗?我们可以这样理解:
1.
std::string
的存储方式
std::string
是一个 C++ 标准库中的类,它的内部实现通常是一个动态分配的字符数组(类似于char[]
),但它并不依赖\0
来表示字符串的结束。
C风格字符串:C语言中的字符串是以
\0
结尾的字符数组,比如char str[] = "Hello";
,它的内存布局是{'H', 'e', 'l', 'l', 'o', '\0'}
。
std::string
:std::string
并不需要\0
来表示字符串的结束,因为它内部会记录字符串的长度(通过size()
或length()
获取)。
2.
std::string
的末尾是否有\0
?
默认情况下:
std::string
的末尾不一定有\0
,因为它不依赖\0
来判断字符串的结束。特殊情况下:如果你通过
c_str()
或data()
方法获取std::string
的内部字符数组,那么返回的字符数组会以\0
结尾。代码示例
#include <iostream> #include <string>int main() {std::string str = "Hello";std::cout << "String: " << str << std::endl;std::cout << "Length: " << str.size() << std::endl; // 输出 5// 使用 c_str() 获取 C风格字符串const char* cstr = str.c_str();std::cout << "C-style string: " << cstr << std::endl; // 输出 "Hello"std::cout << "C-style string length: " << strlen(cstr) << std::endl; // 输出 5return 0; }
解释
str.size()
返回的是字符串的实际长度(5),不包括\0
。
c_str()
返回的 C风格字符串会以\0
结尾,因此strlen(cstr)
也是 5。
3. 为什么
c_str()
会返回以\0
结尾的字符串?
c_str()
是为了兼容 C 风格的字符串操作(比如与 C 函数交互)。C 风格的字符串需要以\0
结尾,因此c_str()
会在内部字符数组的末尾添加一个\0
,并返回指向该数组的指针。注意哦
c_str()
返回的指针指向std::string
的内部数据,不要修改它。如果
std::string
的内容被修改(比如通过append()
、erase()
等操作),c_str()
返回的指针可能会失效。
4.
data()
和c_str()
的区别
c_str()
:返回一个以\0
结尾的 C风格字符串。
data()
:返回指向内部字符数组的指针,不保证以\0
结尾(在 C++11 之前)。从 C++11 开始,data()
和c_str()
的行为一致,都会返回以\0
结尾的字符数组。代码示例
#include <iostream> #include <string>int main() {std::string str = "Hello";const char* cstr = str.c_str();const char* data = str.data();std::cout << "c_str(): " << cstr << std::endl; // 输出 "Hello"std::cout << "data(): " << data << std::endl; // 输出 "Hello"return 0; }
5. 小结
std::string
内部存储的字符串末尾不一定有\0
,因为它通过size()
记录字符串长度。
c_str()
和data()
返回的字符数组会以\0
结尾,这是为了兼容 C 风格的字符串操作。要记住哦,一定不要假设
std::string
的末尾有\0
,除非你通过c_str()
或data()
获取了它的内部字符数组。
6. 常见问题
Q1:为什么
std::string
不需要\0
结尾?因为
std::string
是一个类,它内部会记录字符串的长度(通过size()
或length()
),所以不需要依赖\0
来判断字符串的结束。Q2:如果我自己在
std::string
中插入\0
,会怎么样?
std::string
允许存储\0
,但它不会将其视为字符串的结束符。例如:std::string str = "Hello\0World"; std::cout << str << std::endl; // 输出 "Hello" std::cout << str.size() << std::endl; // 输出 11
str.size()
返回的是 11,因为\0
也被视为一个字符。输出时,
std::cout
会停在第一个\0
处,因此只输出"Hello"
。Q3:如何正确处理包含
\0
的字符串?如果需要处理包含
\0
的字符串,可以使用std::string
的data()
或c_str()
方法,并结合size()
来获取完整的字符串内容。
4. 字符串拼接
拼接字符串非常简单,直接用 +
或者 +=
,比如 "Hello" + " World"
结果是 "Hello World"
。
代码示例
std::string str1 = "Hello";
std::string str2 = "World";
std::string result = str1 + " " + str2; // result = "Hello World"str1 += " C++"; // str1 变成 "Hello C++"std::string str1 = "Hello";
std::string str2 = "World";
std::string result = str1 + " " + str2; // result = "Hello World"str1 += " C++"; // str1 变成 "Hello C++"
术语解释
-
拼接:将两个字符串连接在一起。
5. 比较字符串
可以用 ==
、!=
、<
、>
等运算符来比较字符串,比如 "apple" < "banana"
结果是 true
。
代码示例
std::string str1 = "apple";
std::string str2 = "banana";
if (str1 < str2) {cout << "apple comes before banana";
}
术语解释
-
字典序:字符串的比较规则,类似于字典中单词的排序。
6. 子串操作
用 substr()
方法可以截取字符串的一部分,比如从 "Hello World"
中截取 "World"
。
代码示例
std::string str = "Hello World";
std::string sub = str.substr(6, 5); // 从第6个字符开始,截取5个字符
cout << sub; // 输出 "World"
术语解释
-
子串:字符串的一部分。
7. 查找和替换
用 find()
方法可以查找某个子字符串的位置,用 replace()
方法可以替换字符串中的某一部分。
代码示例
std::string str = "Hello World";
size_t pos = str.find("World"); // 返回子串的起始位置
if (pos != std::string::npos) {cout << "Found at position: " << pos; // 输出6
}std::string str = "Hello World";
str.replace(6, 5, "C++"); // 从第6个字符开始,替换5个字符为"C++"
cout << str; // 输出 "Hello C++"
术语解释
-
查找:在字符串中搜索某个子串。
-
替换:将字符串中的某一部分替换为新的内容。
8. 插入和删除
用 insert()
方法可以在字符串中插入字符或字符串,用 erase()
方法可以删除字符串中的某一部分。
代码示例
std::string str = "Hello";
str.insert(5, " World"); // 在第5个位置插入 " World"(World前有个空格的)
cout << str; // 输出 "Hello World"str.erase(5, 6); // 从第5个字符开始,删除6个字符
cout << str; // 输出 "Hello"
术语解释
-
插入:在字符串的某个位置添加新的内容。
-
删除:移除字符串中的某一部分。
温馨小贴士:
string中的位置是下标的位置吗?
是的!在
std::string
中,位置(position)是基于下标(index)的,而下标是从0
开始计数的。因此,str.insert(5, " World")
中的5
表示的是下标为 5 的位置。1. 下标从 0 开始
在 C++ 中,字符串的下标是从
0
开始的。例如:std::string str = "Hello";
字符串
"Hello"
的下标分布如下:
字符 H e l l o 下标 0 1 2 3 4
字符
H
的下标是0
。字符
o
的下标是4
。
2.
insert
方法的参数
insert
方法的第一个参数是插入位置的下标,第二个参数是要插入的字符串。str.insert(5, " World");
5
是插入位置的下标。
" World"
是要插入的字符串。
3. 插入位置的含义
当下标为
5
时,表示在字符串的第5
个位置插入新的内容。对于字符串
"Hello"
,下标5
实际上是指字符串的末尾,因为"Hello"
的下标范围是0
到4
。插入过程:
原字符串:
"Hello"
(下标0
到4
)。在下标
5
的位置插入" World"
。结果:
"Hello World"
。
4. 验证插入位置
我们可以通过打印字符串的每个字符及其下标来验证插入位置:
#include <iostream> #include <string>int main() {std::string str = "Hello";str.insert(5, " World"); // 在下标 5 的位置插入 " World"std::cout << str << std::endl; // 输出 "Hello World"// 打印每个字符及其下标for (size_t i = 0; i < str.size(); ++i) {std::cout << "下标 " << i << ": " << str[i] << std::endl;}return 0; }
输出:
Hello World 下标 0: H 下标 1: e 下标 2: l 下标 3: l 下标 4: o 下标 5: 下标 6: W 下标 7: o 下标 8: r 下标 9: l 下标 10: d
解释:
下标
5
的位置是空格字符(' '
),这是插入的" World"
的第一个字符。插入后,字符串的长度变为
11
,下标范围是0
到10
。
5. 其他插入位置的例子
例子 1:在字符串中间插入
std::string str = "Hello"; str.insert(2, " World"); // 在下标 2 的位置插入 " World" std::cout << str << std::endl; // 输出 "He Worldllo"
解释:
在下标
2
的位置插入" World"
。插入后,字符串变为
"He Worldllo"
。例子 2:在字符串开头插入
std::string str = "Hello"; str.insert(0, "World "); // 在下标 0 的位置插入 "World " std::cout << str << std::endl; // 输出 "World Hello"
解释:
在下标
0
的位置插入"World "
。插入后,字符串变为
"World Hello"
。
小结
insert
方法的第一个参数是下标位置,从0
开始计数。在
str.insert(5, " World")
中,5
表示下标为5
的位置,即字符串的末尾。插入后,字符串变为
"Hello World"
。
9.std::string
的输入输出
1. 输入
-
使用
cin
输入字符串(遇到空格会停止):std::string str; cin >> str; // 输入 "Hello World",str 只会存储 "Hello"
-
使用
getline()
输入整行字符串:std::string str; getline(cin, str); // 输入 "Hello World",str 存储 "Hello World"
2. 输出
-
使用
cout
输出字符串:std::string str = "Hello World"; cout << str; // 输出 "Hello World"
三、std::string
的底层实现
std::string
的底层通常是一个动态分配的字符数组,它会自动管理内存。当字符串长度增加时,会自动分配更大的内存;当字符串被销毁时,会自动释放内存。
术语解释
-
动态分配:根据需要分配内存,而不是固定大小。
-
内存管理:自动分配和释放内存,避免内存泄漏。
四、std::string
的常用成员函数
函数名 | 功能描述 |
---|---|
size() | 返回字符串的长度 |
length() | 返回字符串的长度 |
empty() | 判断字符串是否为空 |
clear() | 清空字符串 |
c_str() | 返回C风格字符串(const char* ) |
find() | 查找子串 |
replace() | 替换子串 |
substr() | 获取子串 |
insert() | 插入字符或字符串 |
erase() | 删除字符 |
push_back() | 在末尾添加字符 |
pop_back() | 删除末尾字符 |
五、std::string
的注意事项
1. c_str()
的使用
-
c_str()
返回一个C风格字符串(const char*
),常用于与C函数交互。 -
注意:返回的指针指向
std::string
的内部数据,不要修改它。
2. 性能问题
-
std::string
的动态内存分配可能会导致性能开销,但在大多数情况下可以忽略。
3. 与C风格字符串的转换
-
从
std::string
到C风格字符串:使用c_str()
。-
从C风格字符串到
std::string
:直接赋值。
-
六、总结
std::string
是 C++ 中处理字符串的核心工具,功能强大且易于使用。通过掌握它的基本用法和高级特性,可以轻松应对各种字符串处理任务。以下是一些关键点:
-
初始化:支持多种初始化方式,灵活方便。
-
操作:支持拼接、查找、替换、截取等常见操作。
-
性能:自动管理内存,性能足够满足大多数场景。
-
扩展:结合标准库算法(如
std::find
、std::replace
),可以实现更复杂的功能。
下一篇会开始学习vector,让我们一起加油!!!