定义解释
字符串本质上是一个接一个字符的一组字符(字母、数字、符号),这类东西基本上就是文本,所有被称为字符串的东西都是一个文本字符串。字符就像字母符号、数字等以不同的形式呈现。
char类型:是一种数据类型,占用一个字节的内存,它能把指针转换成char型指针,所以可以用字节来做指针运算,它对于分配内存缓冲区也很有用,如果你想分配1K的内存,可以分配1024个char,它对字符串和文本也很有用,因为C++对待字符的默认方式是通过ASCII字符进行文本编码。
在C++中处理字符是:一个字符是一个字节,这就是ASCII可以扩展称UTF-8/UTF-16/UTF-32,以及宽字符串(wide string),字符是可以大于一个字节的,存在两个字节、三个以及四个字节的字符(其他语言)。
如果只有一个字节来表示一个字符,8个比特,这意味着有2^8种可能。如果有16个比特,也就是16位字符编码,这意味着我们有2^16种不同的可能性。
在C++中,只是基础语言,不适用任何库,只是原始数据类型,char是一个字节。当你在C++中使用一个字符串,不是2个字节的宽字符串,普通的字符串使用普通的字符。
字符就是char数据类型,而字符串实际上是字符数组,而数组是一组元素的集合,一组字符组成了字符串或文本,本系列中经常将字符串称为const char*。
代码案例
# include <iostream>
using namespace std;
int main()
{
// char*不意味着是在堆上分配的内存,因此不能通过调用delete来删除这些东西
// 经验法则,如果不用new关键字,就不要使用delete关键字
const char* name = "chen"; //这是C语言风格定义字符串的方式,由于C++有一个库,使得字符串操作变得简单
// 可以不写const,通常写const这样做是不想改变这些值,因为字符串是不可变的,意思是,你不能扩展字符串并使它变大,因为"chen"是一个固定分配的内存块
// 如果想要最大的字符串,它需要执行一个全新的分配并删除旧的字符串
string name1 = "cherno";
string name3 = string("cherno") + "hello"; // 实际上你想将两个const char的数组相加,双引号里面的东西是const char数组,不是真正的字符串,不能把这两个指针相加,不能将两个数组直接相加
// 找到字符串中的文本,可以使用name.find()
bool contains = name3.find("no") != string::npos; //string::npos代表一个不存在的位置,用contains代表结果,name.find()返回的是no所在的首位置
//方法一:分开多行
name1 += "hello"; //将一个指针加到name,name是一个字符串,你把它加到字符串上,+=这个操作符在string类被重载了
cout << name1 << endl;
// 方法二
string name4 = string("cherno") + "hello"; //将两个相加的字符数组的其中一个,显示调用一个string构造函数,相当于创建了一个字符串,然后附加这个字符数组给它
// 一个字符串在内存中是什么样?如何工作的?
// 字符串后面有空终止符,字符串从指针的内存地址开始,继续下去,直到它碰到0
// C++的字符是通过单引号,不是双引号,双引号默认是char*
char name2[7] = { 'C', 'h', 'e', 'r', 'n', 'o', '\0' }; // 这是数组而不是字符串,包含四个字符的数组,这里没有空终止字符
// 内存设置为CC,这实际上是一个数组守卫,让我们直到内存是在我们的分配之外,每当我们在调试模式下分配数组,C标准库或者C++标准库,实际上会插入栈首位之类的东西,这样我们就知道是不是在分配内存之外
cout << name2 << endl;
cin.get();
return 0;
}
const char* name = "chen"; char*不意味着是在堆上分配的内存,因此不能通过调用delete来删除这些东西,可以不写const,通常写const这样做是不想改变这些值,因为字符串是不可变的,意思是,你不能扩展字符串并使它变大,因为"chen"是一个固定分配的内存块。
经验法则,如果不用new关键字,就不要使用delete关键字。
一个字符串在内存中是什么样?如何工作的?
char name2[6] = { 'C', 'h', 'e', 'r', 'n', 'o'}; 这是数组而不是字符串,包含六个字符的数组,这里没有空终止字符
char name2[7] = { 'C', 'h', 'e', 'r', 'n', 'o', '\0' }; 加入空终止字符
1、字符串后面有空终止符,字符串从指针的内存地址开始,继续下去,直到它碰到0
2、不设置空终止字符,打印控制台会直到遇见空终止字符才停下
3、通过查看内存空间,可知道内存中存放的东西,如果内存设置为CC,这实际上是一个数组守卫,让我们知道内存是在我们的分配之外,每当我们在调试模式下分配数组,C标准库或者C++标准库,实际上会插入栈首位之类的东西,这样我们就知道是不是在分配内存之外。
字符数组相加的方法
方法一:分开多行
string name1 = "cherno" + "hello"; 实际上你想将两个const char的数组相加,双引号里面的东西是const char数组,不是真正的字符串,不能把这两个指针相加,不能将两个数组直接相加
name1 += "hello"; 将一个指针加到name,name是一个字符串,你把它加到字符串上,+=这个操作符在string类被重载了
string name1 = "cherno" + "hello";
//方法一:分开多行
name1 += "hello"; //将一个指针加到name,name是一个字符串,你把它加到字符串上,+=这个操作符在string类被重载了
方法二:数组变成字符串
string name4 = string("cherno") + "hello"; 将两个相加的字符数组的其中一个,显示调用一个string构造函数,相当于创建了一个字符串,然后附加这个字符数组给它
C++标准库中的String类
在C++中使用的字符串,你应该使用std::string,它只是一个char数组和一些函数,用来操作它们(char 数组),string有一个构造函数,它接受char*或const char*参数
string与const char*等价,string操作更方便
//两者在内存空间中是一样的
const char* name = "chen";
string name1 = "chen";
string类中有一些方便的函数,类似的有find(),可以寻找字符串中的文本
string name3 = string("cherno") + "hello";
// 找到字符串中的文本,可以使用name.find()
bool contains = name3.find("no") != string::npos;
//string::npos代表一个不存在的位置,用contains代表结果,name.find()返回的是no所在的首位置
字符串在函数中的传递
// 把字符串传递给其他函数
void PrintString(string string)
// 实际上这是个副本,当你将类对象传递给一个函数时,你实际上是在复制这个类(对象),当我做string +="h"操作,它不会影响到传递的原始字符串
{
string += "h";
cout << string << endl;
}
这个函数参数进行传递时,将会被复制。
这显然只是一个只读函数,不修改任何东西,我们只是想把它打印出来,为什莫要复制整个字符串,这意味着我们必须动态地在堆上分配一个全新的char数组来存储我们已经得到的完全相同的文本,这会很慢,字符串复制相当慢
当你传递一个这样的字符串,而且是只读的情况下,确保通过常量引用传递它,引用意味着我们不会复制它
// 此时string变量是不允许修改的
void PrintString(const string& string)
{
cout << string << endl;
}