C++ Prime 第三章 字符串、向量和数组
2022-12-25~2023-03-15
命名空间using声明
- 头文件不应该包含using声明
3.1节练习
- 练习3.1: using std::cout; using std::endl; using std::cin; using std::string; ...... 或者不使用using,仅在需要使用到std中的函数时在前面加上std::
3.2 标准库类型string
- 直接初始化:直接执行初始化方法
- 拷贝初始化:通过等号,将等号右侧的数值拷贝到等号左侧中
- getline:从输入流中读取数据,读取到换行符时就将目前读取到的数据存入到string对象中。
string line;
while (getline(cin, line))
cout < line << endl;
- empty函数:检查string对象是否是一个空字符串,返回对应的布尔值
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line;
while (getline(cin, line)) {
if (line.empty()) {
cout << "empty" << endl;
}
else {
cout << line << endl;
}
}
system("pause");
return 0;
}
- size函数:返回字符串对象的长度,类型不是int是string::size_type,size函数返回的是一个无符号整型数,切记不要和带符号的数据类型进行比较等操作
- 字面值和sgring对象相加:由于历史原因,c++中的字符串字面值并不是标准库中的string对象,所以当字面值和字符串进行相加操作时会自动进行类型转化,当string和字面值相加时必须保证+号两边必须有一个string类型的数据
3.2.2节练习
- 练习3.2:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line;
while (getline(cin, line)) {
if (line.size() == 1) {
cout << line << endl;
}
}
system("pause");
return 0;
}
- 练习3.3: getline遇到换行符就将输入流中的数据保存到string对象中 string的输入运算符,在输入流中会忽略空白字符直到遇到第一个非空白字符为止,后续再遇到空白字符,就将数据保存到string中并停止
- 练习3.4:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line1, line2;
if (line1 == line2) {
cout << "line1 == line2" << endl;
}
else {
if (line1 > line2) {
cout << line1 << endl;
}
else
{
cout << line2 << endl;
}
}
return 0;
}
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line1, line2;
if (line1.size() > line2.size()) {
cout << line1 << endl;
}
else {
cout << line2 << endl;
}
return 0;
}
- 练习3.5:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line1, line2;
while (cin >> line1) {
line2 += line1;
}
cout << line2 << endl;
return 0;
}
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line1, line2;
while (cin >> line1) {
line2 += (line1 + " ");
}
cout << line2 << endl;
return 0;
}
- 逻辑与运算符:只有当与运算符的右侧的对象为真时,编译器才会取检查左侧的对象的情况
3.2.3节练习
- 练习3.6:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line = "1234456asdqwe";
for (auto& c : line) {
c = 'X';
}
cout << line << endl;
return 0;
}
- 练习3.7: 每次都是在修改临时变量所以,运行前后的结果没有变化
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line = "1234456asdqwe";
line = "123133";
for (char c : line) {
c = 'X';
cout << c << endl;
}
cout << line << endl;
system("pause");
return 0;
}
- 练习3.8: while:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line = "123123123asdasd";
decltype(line.size()) line_length = 0;
cout << line_length << endl;
while (line_length != line.size()) {
line[line_length] = 'X';
++line_length;
}
cout << line << endl;
system("pause");
return 0;
}
传统for循环:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line = "123123123asdas";
for (decltype(line.size()) line_length = 0;
line_length < line.size();
line_length++) {
line[line_length] = 'X';
}
cout << line << endl;
system("pause");
return 0;
}
while结构更清晰
- 练习3.9: 实际运行并不会报错,访问空字符的第一个下标元素是空
- 练习3.10:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line1, line2;
cin >> line1;
for (auto& c : line1) {
if (!ispunct(c)) {
line2 += c;
}
}
cout << line2 << endl;
system("pause");
return 0;
}
- 练习3.11: 合法,c的类型是const char&
3.3 标准库类型vector
- vector:表示对象的集合,其中所有对象的类型都相同,每个对象都有一个索引与之对应,索引用来访问对象
- 列表初始化vector对象
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> MyInt1;
vector<int> MyInt2 = MyInt1; // 拷贝初始化
vector<int> MyInt3(MyInt2); // 拷贝初始化?
vector<int> MyInt4 = { 10, 20, 30 }; // 列表初始化
vector<vector<int>> MyInt5 = { MyInt1, MyInt2, MyInt3 }; // 列表初始化
vector<int> MyInt6(10, 1); // MyInt6中包含了10个1
vector<int> MyInt7(10); // MyInt7中包含了10个元素,元素的初始值由元素类型自身决定,int即为0
cout << MyInt7[0] << endl;
vector<string> MyString(10);// MyInt7中包含了10个元素,元素的初始值由元素类型自身决定,string即为空字符串
cout << MyString[0] << endl;
system("pause");
return 0;
}
- 值初始化:创建vector对象时忽略初始值时,此时库会创建一个元素初值并把它赋予给容器中的所有元素,这个元素初值由vector中的元素类型决定,如果是int就为0,如果是string就按照string本身的默认初始化值来即空字符串
- 列表初始化的初始值转化为元素数量:当列表初始化提供的值无法进行初始化操作时,考虑将初始值转化成提供元素数量
vector<int> MyInt(10); // 10个元素,每个元素都初始化为0
vector<string> MyString(10); // 10个元素,每个元素都初始化为空字符串
vector<string> MyString{10}; // 提供的值无法进行初始化操作,vector<string> MyString{10}-->vector<string> MyString(10)
3.3.1节练习
- 练习3.12: (a)正确:ivec保存了一个vector对象的元素 (b)错误,svec和ivec保存的数据类型不同 (c)正确,svec保存了10个字符串"null"
- 练习3.13: (a)没有包含元素 (b)10个元素,值都是0 (c)10个元素,值都是42 (d)1个元素,值是10 (e)2个元素,值是10和42 (f)10个元素,值都是空字符串 (g)10个元素,值都是"hi"
练习3.3.2节练习
- 练习3.14:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> Myint;
int temp;
while (cin >> temp) {
Myint.push_back(temp);
}
system("pause");
return 0;
}
- 练习3.15:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<string> Mystring;
string temp;
while (cin >> temp) {
Mystring.push_back(temp);
}
system("pause");
return 0;
}
3.3.3节练习
- 练习3.16:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> v1;
cout << v1.size() << endl; // 0
vector<int> v2(10);
cout << v2.size() << endl; // 10
for (auto temp : v2) {
cout << temp << endl; // 10个0
}
vector<int> v3(10, 42);
cout << v3.size() << endl; // 10
for (auto temp : v3) {
cout << temp << endl; // 10个42
}
vector<int> v4{ 10 };
for (auto temp : v4) {
cout << temp << endl; // 10
}
vector<int> v5{ 10, 42 };
for (auto temp : v5) {
cout << temp << endl; // 10、42
}
vector<int> v6{ 10 };
for (auto temp : v6) {
cout << temp << endl; // 10
}
vector<string> v7{ 10, "hi"};
for (auto temp : v7) {
cout << temp << endl; // 10hi
}
system("pause");
return 0;
}
- 练习3.17:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<string> s;
string temp;
while (cin >> temp)
{
for (auto& c : temp) {
c = toupper(c);
}
s.push_back(temp);
}
for (auto c : s) {
cout << c << endl;
}
system("pause");
return 0;
}
- 练习3.18: 不合法
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec(1);
ivec[0] = 42;
system("pause");
return 0;
}
- 练习3.19:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec1(10, 42);
vector<int> ivec2{ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 };
vector<int> ivec3 = { 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 };
system("pause");
return 0;
}
- 练习3.20: 相邻:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec;
int temp;
while (cin >> temp) {
ivec.push_back(temp);
}
for (decltype(ivec.size()) i = 0; i < ivec.size() - 1; i++) {
cout << ivec[i] + ivec[i + 1] << endl;
}
system("pause");
return 0;
}
首尾:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec;
int temp;
while (cin >> temp) {
ivec.push_back(temp);
}
decltype(ivec.size()) temp_time = ivec.size() - 1;
for (decltype(ivec.size()) i = 0; i < ivec.size() - 1; i++) {
if (i < temp_time - i) {
cout << ivec[i] + ivec[temp_time - i] << endl;
}
else if (i == (temp_time - i)) {
cout << ivec[i] << endl;
}
}
system("pause");
return 0;
}
- cbegin()、cend()不论容器的对象是不是常量,返回的都是const_iterator
- 箭头运算符->把解引用和访问成员两个操作结合在一起,it->mem==(* it).mem
- 两个对vector对象的操作会导致vectaor的迭代器失效: 1、在范围for循环中向vector添加元素 2、任何一种改变vector容量的操作,都会使vector对象的迭代器失效
3.4.1节练习
- 练习3.21: 基本类似:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> v(5, 42);
for (vector<int>::const_iterator i = v.cbegin(); i != v.cend(); ++i) {
cout << *i << endl;
}
system("pause");
return 0;
}
- 练习3.22:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<string> text{ "hiiiiiiiiiii", "", "123123"};
for (vector<string>::iterator i = text.begin(); i != text.end() && !i->empty(); ++i) {
for (auto& c : *i) {
c = toupper(c);
}
cout << *i << endl;
}
system("pause");
return 0;
}
- 练习3.23:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<string> text{ "hiiiiiiiiiii", "", "123123"};
for (vector<string>::iterator i = text.begin(); i != text.end() && !i->empty(); ++i) {
for (auto& c : *i) {
c = toupper(c);
}
cout << *i << endl;
}
system("pause");
return 0;
}
- 练习3.23:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> my_list(10, 1);
for (vector<int>::iterator i = my_list.begin(); i != my_list.end(); ++i) {
*i = *i * 2;
}
for (vector<int>::iterator i = my_list.begin(); i != my_list.end(); ++i) {
cout << *i << endl;
}
system("pause");
return 0;
}
3.4.2迭代器运算
- 所有标准容器都支持递增运算、和!=、==进行比较
- 迭代器不支持两个迭代器相加运算、支持两个迭代器相减运算
3.4.2节练习
- 练习3.24: 相邻:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> vec{ 1, 2, 3, 4, 5 };
auto beg = vec.begin(), end = vec.end() - 1;
while (beg != end) {
cout << *beg + *(beg + 1) << endl;
++beg;
}
system("pause");
return 0;
}
首尾:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> vec{ 1, 2, 3, 1, 3, 4, 5 };
auto beg = vec.begin(), end = vec.end() - 1;
while (true) {
if (end > beg) {
cout << *beg + *end << endl;
--end;
++beg;
}
else if (end == beg) {
cout << *beg << endl;
break;
}
else {
break;
}
}
system("pause");
return 0;
}
- 练习3.25:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<int> vec(11, 0);
auto beg = vec.begin();
unsigned grade;
while (cin >> grade) {
if (grade <= 100) {
*(beg + (grade / 10)) = *(beg + (grade / 10)) + 1;
cout << *(beg + (grade / 10)) << endl;
}
}
for (auto i : vec) {
cout << i << endl;
}
system("pause");
return 0;
}
- 练习3.26:两个迭代器不支持相加运算
3.5 数组
- 存放相同类型数据的容器,数组大小确定不变
- 数组的声明:a[d],a为数字名字,d是一个常量表达式代表数组中元素的个数
- 数组定义时必须指定数组的类型,不允许使用auto关键字
- 字符数组的特殊性:使用字符串字面值进行字符数组初始化时,需要注意字面值结尾处还有一个空字符 const char a4[6] = "Daniel"; // 错误,Daniel隐形的包含了一个空字符串
- 数组不允许拷贝和赋值
3.5.1节练习
- 练习3.27: (a):非法,buf_size不是常量 (c):非法,tet_size函数的返回值不是一个常量 (d):非法,fundamental隐形的包含了一个空字符,没有空间来存放空字符
- 练习3.28:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int sa[10]; // 0
string ia[10]; // 空字符
int main()
{
int sa2[10]; // 未定义
string ia2[10]; // 空字符
system("pause");
return 0;
}
- 练习3.29: 缺点: 1、数组的维度大小在创建时必须已确定,并且数组无法进行扩容 2、定义数组时必须显式指定数组的类型,无法使用auto等关键字 3、数组无法进行拷贝、赋值操作
3.5.2 节练习
- 练习3.30:数组的大小是10,for循环中从0开始遍历,所以for循环结束的边界值应该array_size-1,而不是array_size
- 练习3.31:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){
int int_array[10] = {};
int temp = 0;
for (auto& a : int_array) {
a = temp;
temp++;
}
return 0;
}
- 练习3.32: 拷贝:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){
int int_array[10] = {};
int temp = 0;
for (auto& a : int_array) {
a = temp;
temp++;
}
int int_array1[10] = {};
int temp1 = 0;
for (auto& a : int_array1) {
a = int_array[temp1];
temp++;
}
return 0;
}
vector:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){
vector<int> vet_int(10);
int temp = 0;
for (auto &a : vet_int) {
a = temp;
temp++;
}
vector<int> vec_int1 = vet_int;
return 0;
}
- 练习3.33:如果scores在函数体外,编译器会将scores的数值都默认初始化为0,如果在函数体内,socres内的数值未定义,运行程序结果未知
3.5.3 指针和数组
- 很多用到数组名的地方,编译器都会自动的将数组名替换成一个指向数组首元素的指针
int ia[] = { 0, 1, 2 };
cout << ia[0] << endl; // 0
auto ia2(ia);
*ia = 10;
cout << ia[0] << endl; // 10
- 当使用decltype关键字对数组进行数据类型推导时,数组名不会被替换成指向数组首元素的指针
int ia[] = { 0, 1, 2 };
decltype(ia) ia3 = { 1, 2, 3 }; // decltype返回的类型是由0,1,2构成的数组类型
- 标准库函数begin、end
int ia[] = {0, 1, 2};
int *beg = begin(ia); //beg指针指向ia的首元素
int *last = end(ia); //last指针指向ia的尾元素的下一位置,尾后指针无法执行解引用和递增操作
- 两个指针相减的结果是他们之间的距离,返返回的结果类型是名为ptrdiff_t的带符号类型数据
- 对数组执行下标运算其实是对指向数组元素的指针进行下标运算
int ia[] = {1, 2, 3}
int i = ia[2]; // 对数组进行下标运算
int *p = ia;
i = *(p + 2); // 等价于int i = ia[2]
3.5.3练习
- 练习3.34: 程序功能:让p1指针前进或后退p2到p1之间个元素,当p1为常量指针时程序非法
- 练习3.35:
using namespace std;
#include<iostream>
int my_array[5] = {10, 20, 30, 40, 50};
int main()
{
int *p = my_array;
int* last = end(my_array);
while (p != last)
{
*p = 0;
p++;
}
for (auto temp : my_array)
{
cout << temp << endl;
}
system("pause");
return 0;
}
- 练习3.36 判断数组:
using namespace std;
#include<iostream>
int main()
{
int my_array1[6] = { NULL, 20, 30, 40, 50 };
int my_array2[6] = { NULL, 20, 30, 40, 50 };
if (sizeof(my_array1) == sizeof(my_array2))
{
bool same_flag = true;
int max_num = sizeof(my_array1) / sizeof(my_array1[0]);
for (int i = 0; i < max_num; i++)
{
if (my_array1[i] != my_array2[i])
{
same_flag = false;
break;
}
}
if (same_flag == true)
{
cout << "相等" << endl;
}
else
{
cout << "不相等" << endl;
}
}
else
{
cout << "不相等" << endl;
}
system("pause");
return 0;
}
判断vector
using namespace std;
#include<iostream>
#include<vector>
int main()
{
vector<int> my_vector1 = { 10, 20, 30, 40, 50 ,60 };
vector<int> my_vector2 = { 10, 20, 30, 40, 50 };
if (my_vector1.size() == my_vector2.size())
{
bool same_flag = true;
for (int i = 0; i < my_vector1.size(); i++)
{
if (my_vector1[i] != my_vector2[i])
{
same_flag = false;
break;
}
}
if (same_flag == true)
{
cout << "相等" << endl;
}
else {
cout << "不相等" << endl;
}
}
else
{
cout << "不相等" << endl;
}
system("pause");
return 0;
}
3.5.4 练习
- 练习 3.37:将ca数组中的元素逐行打印
- 练习 3.38:指针相减代表距离、指针加减数字代表前进后退
- 练习 3.39:
using namespace std;
#include<iostream>
#include<string>
int main()
{
string s1 = "12345";
string s2 = "22345";
if (s1 == s2)
{
cout << "相等" << endl;
}
char ca1[] = { 'c', '#', '1233', '1233', '\0' };
char ca2[] = { 'c', '#', '1233', '1233', '\0' };
int ret = strcmp(ca1, ca2);
ret = 0;
switch (ret)
{
case 0: {
cout << "相等" << endl;
break;
}
case 1: {
cout << "大于" << endl;
break;
}
case -1: {
cout << "小于" << endl;
break;
}
default:
break;
}
system("pause");
return 0;
}
- 练习 3.40
using namespace std;
#include<iostream>
int main()
{
const char ca1[] = "c++";
const char ca2[] = "c#";
char ca3[20];
strcpy_s(ca3, ca1);
strcat_s(ca3, " ");
strcat_s(ca3, ca2);
cout << ca3 << endl;
system("pause");
return 0;
}
3.5.5 节练习
- 练习 3.41:
using namespace std;
#include<iostream>
#include<vector>
int main()
{
int arr[] = { 1, 2, 3, 4 };
vector<int> my_arr(begin(arr), end(arr));
for (auto& temp : my_arr)
{
cout << temp << endl;
}
system("pause");
return 0;
}
- 练习 3.42:
using namespace std;
#include<iostream>
#include<vector>
int main()
{
vector<int> my_arr = {1, 2, 3 ,4};
int arr[4];
for (int i = 0; i < my_arr.size(); i++)
{
arr[i] = my_arr[i];
}
for (auto temp : arr)
{
cout << temp << endl;
}
system("pause");
return 0;
}
3.6节练习
- 练习3.43:
using namespace std;
#include<iostream>
#include<vector>
int main()
{
// 版本1范围for
int ia[3][4] = {
{1, 2, 3 ,4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int(&p)[4] : ia)
{
for (int& q : p)
{
cout << q;
}
cout << endl;
}
// 版本2普通for下标
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
cout << ia[i][j];
}
cout << endl;
}
// 版本3普通for指针
for (int (*p)[4] = ia; p < ia+3; p++)
{
for (int* q = *p; q < *p + 4; q++)
{
cout << *q;
}
cout << endl;
}
system("pause");
return 0;
}
- 练习3.44
using namespace std;
#include<iostream>
#include<vector>
int main()
{
// 版本1范围for
int ia[3][4] = {
{1, 2, 3 ,4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
using int_array = int[4];
for (int_array* i = ia; i < ia + 3; i++)
{
for (int* p = *i; p < *i + 4; p++)
{
cout << *p;
}
cout << endl;
}
system("pause");
return 0;
}
- 练习3.44
using namespace std;
#include<iostream>
#include<vector>
int main()
{
// 版本1范围for
int ia[3][4] = {
{1, 2, 3 ,4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (auto i = ia; i < ia + 3; i++)
{
for (auto p = *i; p < *i + 4; p++)
{
cout << *p;
}
cout << endl;
}
system("pause");
return 0;
}
C++Prime学习笔记 文章被收录于专栏
勇敢和愚蠢只有一剑之差