C++零基础到工程实战(3.2.1):string 入门——字符、字符串字面量、const char* 与 string 对象与内存管理详解
目录一、本节学习内容概要图二、前言2.1 初学 string 最容易混淆的地方1char、string、字符串字面量、const char*、std::string 到底有什么区别2单引号和双引号的区别3const char* 和 string 不是一回事4std::string str Hello; 里哪个才是对象5为什么这一节要先讲“原理”再讲“语法”三、string 的本质与基础定义3.1 什么是 std::string1string 不是普通变量那么简单2为什么工程里更常用 string四、字符 char、字符串字面量、string 三者关系4.1 字符 char1char 表示单个字符2char 占 1 个字节3中文字符和英文字符的区别4.2 字符串字面量1双引号中的内容叫做字符串字面量。2它不是 std::string 对象。4.3 字符串字面量本质上可以理解为一个字符数组常量 const char[ ]。4.4 string五、const char* 与 std::string 的区别5.1 const char* 是什么1p 是一个指针变量2这个地址指向什么3const char* 中的 const 限制了什么4更严谨地怎么理解 const char*5.2 什么是 std::string 类对象1std::string2Hello3str4为什么叫“对象”六、std::string str1{test string 1}; 到底发生了什么6.1 右边的内容是什么6.2 左边的 str1 是什么6.3 这句代码整体怎么理解1先有右边的字符串字面量2再调用 std::string 的构造过程3从此以后由 str1 来管理这段字符串七、string 的内存该怎么入门理解7.1 对象本身通常在哪里7.2 对象本体里一般有什么7.3 字符内容一定在堆区吗1局部 string 对象本身通常在栈区2字符内容由对象内部统一管理3较长字符串很多时候会借助堆空间4较短字符串也可能直接存在对象内部7.4 重新赋值时发生了什么1如果原有空间够用2如果原有空间不够3旧内容怎么处理由 string 自己负责一般是释放该空间内存7.5 用另一个 string 初始化时发生了什么1内容一样2对象不同3修改一个不会影响另一个八、小结8.1 这一篇最核心的内容1概念上要分清2理解上要建立的几个关键点8.2 一句话总结一、本节学习内容概要图二、前言在 C 中std::string是最常用的数据类型之一。前面学习整数、浮点数、布尔类型时我们处理的大多是数值但真正写程序时还会经常遇到用户名、文件路径、命令、文本内容、日志信息等这些都离不开字符串。很多初学者刚学string时容易把下面这些概念混在一起2.1 初学 string 最容易混淆的地方1char、string、字符串字面量、const char*、std::string到底有什么区别1char表示单个字符。2string表示一串字符组成的文本。3一个只能放一个字符一个可以放很多字符。4双引号里的内容是字符串字面量。5const char*是字符指针保存的是字符序列首地址。6std::string是标准库中的字符串类定义出来的是字符串对象。例如char c A; string str Hello;这里c只能保存一个字符而str保存的是整个字符串。2单引号和双引号的区别1单引号 用来表示单个字符。2双引号 用来表示字符串字面量。例如char c a; string str a;虽然看起来都像字母 a但含义完全不同。3const char*和string不是一回事1string是 C 标准库里的字符串对象。2const char*是一个指针指向一段字符数据。3初学阶段可以把string理解成“更高级、更安全、更方便用的字符串类型”。所以这篇文章就围绕这些基础问题把string的常用功能系统讲清楚。4std::string str Hello;里哪个才是对象这里std::string是类类型Hello是字符串字面量str才是根据std::string这个类创建出来的字符串对象5为什么这一节要先讲“原理”再讲“语法”因为如果一开始就直接上size()substr()find()replace()很多人虽然能照着写但并没有真正理解双引号里的内容到底是什么const char*为什么不能随便改string对象和字符串字面量有什么区别std::string str1{test string 1};这句代码底层到底做了什么所以这一篇我们先不急着讲大量函数而是先把string的基础概念和内存理解讲清楚为后面第二篇做铺垫。三、string 的本质与基础定义3.1 什么是std::string在 C 中要使用字符串对象通常需要先包含头文件#include string如果要输出还要加#include iostream using namespace std;然后就可以定义字符串string str;这表示创建了一个字符串对象str。1string 不是普通变量那么简单1string本质上是标准库提供的一个类。2它不是单纯的一块字符数组而是一个“对象”。3这个对象内部帮我们管理字符串内容所以使用起来比字符数组方便得多。2为什么工程里更常用 string1支持直接赋值。2支持直接比较。3支持长度获取、截取、拼接、查找、替换。4不用手动管理字符串空间。这也是现代 C 中大家更常用string而不是 C 风格字符数组的原因。四、字符char、字符串字面量、string 三者关系这几个概念一定要分清。4.1 字符char1char 表示单个字符例如char c1 A; char c2 b; char c3 \n; char c4 \t; char c5 \\;这里a、A是普通字符。\n表示换行。\t表示制表符也就是 Tab。\\表示字符\本身因为反斜杠是转义符所以写一个反斜杠时要写成两个。2char占 1 个字节在 C 中char占 1 个字节。通常一个字节等于 8 位 bit。对于英文字符来说一个char通常就够表示一个字符。比如Ab3#3中文字符和英文字符的区别英文字符通常 1 个字节就能表示但中文字符占几个字节和编码方式有关。例如常见情况1GBK 编码下很多中文字符通常占 2 个字节。2UTF-8 编码下很多中文字符通常占 3 个字节。所以后面学习string.size()时如果字符串中有中文结果不一定等于你看到的汉字个数因为size()更接近统计字节数。4.2 字符串字面量1双引号中的内容叫做字符串字面量。例如Hello test string 1 abc2它不是std::string对象。这一点一定要分清。双引号里的内容首先是字符串常量不是string类对象。像Hello它首先不是string对象而是程序中的一段字符串常量。也就是说它可以作为构造string的原始内容但它自己本身不是std::string4.3 字符串字面量本质上可以理解为一个字符数组常量 const char[ ]。比如Hello本质可以理解为const char[6]为什么是 6因为除了Hello还会在末尾自动补一个\0也就是字符串结束符。所以Hello在内存里更准确地看是H e l l o \0对应下标是0 1 2 3 4 5也就是说访问是从 0 开始的最后那个\0也占一个位置。4.4string例如string str Hello;这表示用字符串字面量Hello去构造一个string对象str。五、const char*与std::string的区别5.1const char*是什么很多初学者看到下面这句代码时会有些模糊const char* p Hello;这句话要拆开理解。1p是一个指针变量也就是说p本身保存的是一个地址。2这个地址指向什么它指向字符串字面量Hello的首字符地址也就是H所在位置。3const char*中的const限制了什么它限制的是通过这个指针不能修改它指向的字符内容。也就是说下面这种写法是不允许的const char* p Hello; // p[0] X; // 不允许因为Hello是字符串字面量通常位于只读区域不应该被修改。4更严谨地怎么理解const char*你可以把它理解成const char*是指向常量字符序列首地址的指针。也就是说1它自己是个变量。2变量里存的是地址。3地址指向一段连续排列的char数据。4这段数据不能通过该指针修改。5.2 什么是std::string类对象这是理解string最关键的一步。例如std::string str Hello;这里有三个东西1std::string它是标准库里的一个类类型。2Hello它是字符串字面量它是写在程序里的固定文本常量不是对象。3str它才是根据std::string这个类创建出来的字符串对象。所以可以这样理解双引号里的Hello是原始字符串常量str才是真正会管理字符串的对象。4为什么叫“对象”因为std::string不是基本类型它是一个类。而根据类创建出来的变量就叫对象。例如std::string str Hello;这个str对象可以调用很多成员函数str.size(); str.empty(); str.substr(1, 3); str.find(ll); str.replace(0, 2, Hi);这也是它和普通字符串字面量最大的区别字符串字面量只是文本常量string对象则是“会管理文本并提供操作功能的对象”六、std::string str1{test string 1};到底发生了什么6.1 右边的内容是什么来看这句代码std::string str1{test string 1};右边的test string 1首先是字符串字面量本质上可以粗略理解为const char[14]因为1它有 13 个可见字符。2结尾还自动带了一个\0。所以总共是 14 个字符位置。6.2 左边的str1是什么左边的str1是一个std::string类型对象。它不是简单的字符数组而是标准库中的字符串对象。也就是说str1不是“直接等于右边那串字符”而是一个类对象它会把右边那段文本内容保存并管理起来。6.3 这句代码整体怎么理解这句代码可以这样理解1先有右边的字符串字面量程序先拿到test string 1这段固定字符内容。2再调用std::string的构造过程用这段字符内容来构造一个新的std::string对象str1。3从此以后由str1来管理这段字符串也就是说从用户使用角度看后续操作的是str1而不是原始字符串字面量本身。所以可以简单概括为右边是原始字符常量左边是根据这些内容构造出来的字符串对象。七、string 的内存该怎么入门理解7.1 对象本身通常在哪里如果是在函数内部定义int main() { std::string str1{test string 1}; }那么str1这个对象本身通常是局部对象通常位于栈区。也就是说栈里放的是这个string对象本体。7.2 对象本体里一般有什么std::string对象本身通常不只是单纯的一串字符。它内部一般会保存一些管理信息例如可以粗略理解为1当前字符串长度2当前容量3字符数据的位置4其他实现相关信息所以你可以把string对象理解成一个“字符串管理者”。7.3 字符内容一定在堆区吗很多入门资料会直接说string对象在栈区字符串内容在堆区。这个说法作为入门理解可以先接受但它并不完全严谨。更准确地说1局部string对象本身通常在栈区这是比较容易理解的部分。2字符内容由对象内部统一管理它不一定永远固定在某个地方而是由string自己决定怎么存。3较长字符串很多时候会借助堆空间这也是很多教材为什么会简单写成“内容在堆区”的原因。4较短字符串也可能直接存在对象内部现代很多string实现会采用SSOSmall String Optimization小字符串优化。意思就是如果字符串比较短可能直接保存在对象内部而不一定额外去堆上申请空间。所以更稳妥的说法应该是std::string对象本身通常位于栈区而字符内容由对象内部统一管理较长字符串常常会借助堆空间保存但较短字符串也可能直接保存在对象内部。7.4 重新赋值时发生了什么例如std::string str1{test string 1}; str1 test string 2;这表示给str1重新赋值。从底层理解可以粗略看成1如果原有空间够用string可能直接在原有存储区域中更新内容。2如果原有空间不够string可能重新申请更大的空间再把新内容保存进去。3旧内容怎么处理由string自己负责一般是释放该空间内存这也是std::string的优势所在你不需要自己去申请、扩容、释放字符串空间string会自动管理。7.5 用另一个 string 初始化时发生了什么例如std::string str1{abc}; std::string str2{str1};这表示用str1的内容来初始化str2。1内容一样初始化完成后str2和str1的内容一样。2对象不同它们是两个不同的string对象。3修改一个不会影响另一个例如str2 xyz;不会让str1也变成xyz。所以可以理解为string的复制本质上是内容复制而不是两个变量共用一份文本别名。八、小结8.1 这一篇最核心的内容1概念上要分清1char表示单个字符。2双引号里的内容是字符串字面量。3字符串字面量本质上可以粗略理解为const char[]。4它结尾会自动带一个\0。5const char*是指向常量字符序列首地址的指针。6std::string是标准库中的字符串类。7std::string str Hello;里的str才是真正的字符串对象。2理解上要建立的几个关键点1双引号里的内容不是string对象。2string对象是根据std::string这个类创建出来的。3string对象会自己管理字符串内容。4局部string对象本身通常在栈区。5字符串内容由对象内部统一管理较长时常借助堆空间较短时也可能直接存在对象内部。8.2 一句话总结如果说字符串字面量只是“写在程序里的原始文本”那么std::string对象就是“把这段文本保存起来并且负责管理、操作它的类对象”。这部分概念一旦真正理顺后面学习size()capacity()substr()empty()find()replace()就会顺很多因为你已经知道自己操作的到底是什么了。