C++ primer 5th 第3章 字符串、向量和数组
=====================================================================
第3章 字符串、向量和数组074页 命名空间using的声明
=====================================================================
//QQ108201645编写
#include <iostream>
//using声明,当使用cin时,从命名空间获取它
using std::cin;
int main()
{
int i;
cin >> i;//正确,cin和std::cin 含义相同
//cout << i;//错误:没有对应的using声明(“cout”: 未声明的标识符)
std::cout << i;//正确,显示的从std中使用cout
return 0;
}
=====================================================================
第3章 字符串、向量和数组074-075页 命名空间using的声明
=====================================================================
//QQ108201645编写
#include <iostream>
//通过下列using声明,我们可以使用标准库中的名字
using std::cin;
using std::cout;using std::endl;
int main()
{
cout << "Enter two numbers: " << endl;
int v1, v2;
cin >> v1 >> v2;
cout << "The sum of " << v1 << " and " << v2
<< " is " << v1 + v2 << endl;
return 0;
}
=====================================================================
第3章 字符串、向量和数组075页 标准库string
=====================================================================
//QQ108201645编写
#include <string>//包含string
#include <iostream>
using std::string;//使用std的命名空间的string
using std::cout;using std::cin;using std::endl;
int main()
{
//定义和初始化
string s1; //默认初始化空字符串
string s2 = s1;//s2是s1的副本
string s3 = "hiya";//s3是这个字符串的副本(把“hiya”拷贝(或赋值)给s3)
string s4(10, 'c');//s的内容"是cccccccccc"
cout << "s1 = " << s1 << "\t s2 = " << s2
<< "\t s3 = " << s3 << "\t s4 = " << s4 << endl;
system("pause");
return 0;
}
//输出结果
//s1 = s2 = s3 = hiya s4 = cccccccccc
=====================================================================
第3章 字符串、向量和数组076页 标准库string初始化方法
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>//包含string
using std::string;//声明使用命名空间的string
using std::cout; using std::cin; using std::endl;
int main()
{
//string初始化的几种方式
string s1;//默认初始化
//string s2(s1);//s2是s1的副本//“s2”: 重定义;多次初始化,与下一条只能使用一个
string s2 = s1;//(调用operator=)等价于s2(s1),s2是s1的副本
string s3("value");
//s3是字面值"value"的副本,除了字面值的最后的空白字符(默认字符串会加上'\0')
int n = 6;
string s4(n, 'c');//把s4初始化为由连续n个字符'c'组成字符串
//如果用=号,实际上是拷贝初始化
string s5 = "hiya";//拷贝初始化
string s6("hiya");//直接初始化
string s7(10, 'c');//直接初始化,s7的内容是cccccccccc
//string s8 = string(10, 'c');//拷贝初始化,s8的内容是cccccccccc
//(“s8”: 重定义;多次初始化)与下面的s8可以任选一个注释
string temp(10, 'c'); //temp的内容是cccccccccc
string s8 = temp;//将temp的字符串拷贝给s8
system("pause");//暂停
return 0;
}
=====================================================================
第3章 字符串、向量和数组075页 标准库string对象上的操作
=====================================================================
//QQ108201645编写
比如用string创建了三个s、s1,s2 | |
表3.2:string的操作 | |
os<<s | 将s写到输出流os当中 |
is>>s | 从is中读取字符串赋给s,字符串以空白分隔 |
getline(is,s) | 从is记取一行赋给s,返回is |
s.empty() | s为空返回true,否则返回false |
s.size() | 返回s字符串中的字符个数 |
s[n] | 返回s中第n个字符的引用,位置从0计起 |
s1+s2 | 返回s1和s2连接后的结果 |
s1=s2 | 用s2的副本代替s1中原来的字符串(补充:把s2拷贝给s1) |
s1==s2 | 如果s1与s2中所包含字符串一样,则相等 |
s1!=s2 | 如果s1不等于s2的字符串,string对象相等性,判断对字符串的大小写敏感 |
<,<=,>,>= | 利用字符在字典中的顺序进行比较,且区分大小写 |
附加内容 | |
s.size() | 得到字符串大小 |
s.length() | 同上 |
s.empty() | 判断是否是空 |
s.substr(0,5) | 从s[0]位置截取到s[5]的字符串(截取字符串) |
s.find('a',2) | 从s[2]位置开始查找a字符,定义方式string::size_type ps=查找,string::npos表示没找到 (在字符串中查找字符或者字符串) |
s.rfind('a',2) | 从s的倒数2位置开始查找a字符(反向查找) |
s.replace(3,4,"abc") | 把s[3]-s[4]位置的字符串替换成abc(替换) |
s.compare(s1) | 与s1对象的字符串进行比较(比较字符串) |
s.insert(6,"abc") | 从s[6]位置插入abc,并且原先6之后的往后移动(插入字符串) |
s.append("abc") | 在末尾追加abc(追加字符串) |
s.swap(s1) | 与s1的字符串交换(交换字符串) |
重载运算符 | [],+=,=,+,>,<,>=,<=,!=,==,>>,<<等 |
=====================================================================
第3章 字符串、向量和数组077页 读写string对象
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
using std::string;//声明使用string
using std::cin; using std::cout; using std::endl;
int main()
{
string s;//s默认为空
cin >> s;//将string对象读入s,遇到空白或回车停止
cout << s << endl;//输出s
//如果接收" hello"则输出"hello"输出中没有任何空格
string s1, s2;
cin >> s1 >> s2;//把第一个输入读到s1中,第二个输入读到s2中
cout << s1 << s2 << endl;//打印两个string对象
system("pause");
return 0;
}
=====================================================================
第3章 字符串、向量和数组077~079页 读取未知量的string对象
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
using std::string;//声明使用string
using std::cin; using std::cout; using std::endl;
int main()
{
string word;
while (cin >> word)//反复读取,直到输入文件末尾(ctrl+z然后回车)
cout << word << endl;//逐个输出单词,每个单词后面紧跟一个换行
system("pause");//暂停
return 0;
}
=====================================================================
#include <string>
#include <iostream>
using std::string;//声明使用string
using std::getline;
using std::cin; using std::cout; using std::endl;
int main()
{
string line;
while (getline(cin,line))//每次读取一行,直到输入文件末尾(ctrl+z然后回车)
cout << line << endl;//逐个输出单词,每个单词后面紧跟一个换行
system("pause");//暂停
return 0;
}
=====================================================================
#include <string>
#include <iostream>
using std::string;//声明使用string
using std::getline;
using std::cin; using std::cout; using std::endl;
int main()
{
string line;
while (getline(cin,line))//每次读取一行,直到输入文件末尾(ctrl+z然后回车)
if(!line.empty())//判断line不为空(line.empty()为空返回是0,加上!后,返回true),则打印出来
cout << line << endl;//逐个输出单词,每个单词后面紧跟一个换行
system("pause");//暂停
return 0;
}
=====================================================================
#include <string>
#include <iostream>
using std::string;//声明使用string
using std::getline;
using std::cin; using std::cout; using std::endl;
int main()
{
string line;
while (getline(cin,line))//每次读取一行,直到输入文件末尾(ctrl+z然后回车)
if(line.size()>80)//判断line.size()>80个字符返回true,则打印出来,否则不打印
cout << line << endl;//逐个输出单词,每个单词后面紧跟一个换行
system("pause");//暂停
return 0;
}
=====================================================================
#include <string>
#include <iostream>
using std::string;//声明使用string
using std::getline;
using std::cin; using std::cout; using std::endl;
int main()
{
string line;
getline(cin, line);
auto len = line.size();
cout << "len = "<<len << endl;
cout<<"类型:"<<typeid(len).name()<<endl;//到本章节还未学到,仅供参考,输出这个变量的类型
string::size_type sp;
cout << "类型:" << typeid(sp).name() << endl;
system("pause");//暂停
return 0;
}
=====================================================================
第3章 字符串、向量和数组080~082页 读取未知量的string对象
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
using std::string;//声明使用string
using std::cin; using std::cout; using std::endl;
int main()
{
string str = "Hello";
string phrase = "Hello World";
string slang = "Hiya";
//两个字符串对应位置相同,长度不同,比较的是长短,则str小于phrase
if(str<phrase)cout<<"str比phrase小"<<endl;
//两个字符串对应不同,长度不同,比较的是第一对字符串相异的结果,i>e
if (slang>str)cout<<"slang比str大"<<endl;
if (slang > phrase)cout << "slang比str大" << endl;
//连接字符串
string st1(10, 'c'), st2;//st1内容是10个c,st2是空字符串
cout<<"st1 = "<<st1<<endl;
st1 = st2;//赋值:把s2的副本替换到st1的内容
string s1 = "hello", s2 = "world";//在s1和s2中没有标点符号
string s3 = s1 + ", " + s2 + "\n";
cout << "s3 = "<<s3 << endl;
string s4 = s1 + ", ";//正确,把一个string对象与一个字相加
//string s5 = "hello" + ", ";//错误两个运算对象中。其中要有一个必须是string
string s6 = s1 + ", " + "world";
//string s7 = "hello" + ", " + s2;//错误:不能把字面值直接相加
string tmp = s1 + ",e ";//正确,加法运算有一个对象string
s6 = tmp + "world";//正确
//string s7 = ("hello" + ", " )+ s2;//错误:不能把字面值直接相加
getchar();
return 0;
}
=====================================================================
第3章 字符串、向量和数组082页
表3.3:cctype头文件中的函数 | |
函数名称 | 返回值 |
isalnum() | 如果参数是字母数字,既字母或数字,该函数返回true |
isalpha() | 如果参数是字母,该函数返回true |
iscntrl() | 如果参数是控制字符,该函数返回true |
isdigit() | 如果参数是数字(0~9),该函数返回true |
isgraph() | 如果参数是除空格之外的打印字符,该函数返回true |
islower() | 如果参数是小写字母,该函数返回true |
isprint() | 如果参数是打印字符(包括空格),该函数返回true |
ispunct() | 如果参数是标点符号,该函数返回true |
isspace() | 如果参数是标准空白字符,如空格、进纸、换行符、回车、水平制表符或垂直制表符,该函数返回true |
isupper() | 如果参数是大写字母,该函数返回true |
isxdigit() | 如果参数是十六进制数字,既0~9、a~f或A~F,该函数返回true |
tolower() | 如果参数是大写字母,则返回小写字母,否则返回该参数 |
toupper() | 如果参数是小写字母,则返回大写字母,否则返回该参数 |
=====================================================================
第3章 字符串、向量和数组083页 使用基于范围的for语句
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
#include <cctype>
using std::string;//声明使用string
using std::cin; using std::cout; using std::endl;
using std:: ispunct;
int main()
{
string str("some string");
//范围for(range for)语句
//每行输出str中的一个字符
for (auto c:str)//自动推导类型,进行迭代
{
cout << c<< endl;
//用代码测试这是固定地址,猜测:在内部迭代后传给c并输出打印
}
//书中说明:通过auto关键字,让编译器决定变量c的类型,每次迭代,str
//的下一个字符被拷贝给c,因此这个循环可以读作对字符串str中的每个字符赋值给c
//执行某某操作,既输出一个字符
//-----------------------
//输出字符串中的标点符号个数
string s("Hello World!!!");
//punct_cnt的类型和s.size的返回类型一样参见62页
decltype(s.size()) punct_cnt = 0;//选择s.size()返回操作数的类形,不计算表达式
cout<<"类型:"<<typeid(punct_cnt).name()<<endl;
for (auto c:s)
if (ispunct(c))//如果参数是标点符号,该函数返回true
{
++punct_cnt;//如果有标点符号则++
}
cout<<punct_cnt<<" punctuation characters in "<<s<<endl;
getchar();
return 0;
}
=====================================================================
第3章 字符串、向量和数组083~85页 使用基于范围的for语句
=====================================================================
//QQ108201645编写
#include <string>
#include <cctype>
#include <iostream>
using std::string;//声明使用string
using std::cin; using std::cout; using std::endl;
using std::toupper; using std::isspace; //声明使用cctype中的函数
int main()
{
//改变小写转大写
string s("Hello world!!!");
for (auto &c:s)//对于s中的每个字符(注意:c是引用)
{//每次迭代时,变量c引用string对象s的下一个字符,赋值给c也就是在改变s中对应的字符的值
c = toupper(c);//小写转大写
}
cout << "s = "<<s << endl;
//下标输出第一个字符
if(!s.empty())//确保有字符要输出,空返回顾,非空返回0,加上!0==1
cout<<"s[0] = "<<s[0]<<endl;
string s1("some string");
if (!s1.empty())
{
s[0] = toupper(s[0]);//为s的第一个字符赋一个新值,小写转大写后赋给s[0]
}
cout<<"s1 = "<<s1<<endl;
/*下面意思是decltype的index类型是s1.size()返回的类型,初始等于0,条件是index不等于s1的字符串的长度、
并且s1单个字符里不是空白字符。
书面:依次处理s中的字符直到我们处理完全部字符或遇到一个空白*/
for (decltype(s1.size())index=0;index!=s1.size()&&!isspace(s1[index]);++index)
{
s1[index] = toupper(s1[index]);//把当前字符转大写
}
cout << "s1 = " << s1 << endl;
getchar();
return 0;
}
=====================================================================
#include <string>
#include <iostream>
using std::string;//声明使用string
using std::cin; using std::cout; using std::endl;
int main()
{
const string hexdigits = "0123456789ABCDEF";//可能的十六进制数字
cout << "Enter a series of numbers between 0 and 15"
<< " separated by spaces. Hit ENTER when finished: "
<< endl;
string result;//用于保存十六进制
string::size_type n;//从保存的输入流读取的数
while (cin>>n)//循环接收输入的数,直接ctrl+z回车结束
{
if (n<hexdigits.size())//忽略无效输入
{
result += hexdigits[n];//得到对应的十六进制数字
}
}
cout<<"Your hex number is: "<<result<<endl;
/*
输出结果
Enter a series of numbers
inished:
12 0 5 15 8 15
^Z
Your hex number is: C05F8F
*/
system("pause");
return 0;
}
=====================================================================
第3章 字符串、向量和数组086页
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
using std::string;//声明使用string
using std::cin; using std::cout; using std::endl;
using std::getline;
int main()
{
//练习3.6
string buf = "abcde";
for (auto &c : buf)//对于s中的每个字符(注意:c是引用)
//每次迭代时,变量c引用string对象s的下一个字符,赋值给c也就是在改变s中对应的字符的值
c = 'x';
cout << buf << endl;
//输出xxxxx
//练习3.7
char str[] = "12345";
for (auto &d : str)
d = 'x';
cout << str << endl;
//输出xxxxxx烫烫烫烫烫€ ?
//练习3.10
cout << "编写一段程序,读入包含标点符号的字符串,去除标点符号后输出" << endl;
string newstr;
getline(cin, newstr);
int len = newstr.length();
for (int i = 0; i < len; ++i)
{
if (ispunct(newstr[i]))//当 当前位置是标点符号时进入去除标点符号处理
{
newstr = newstr.replace(i, len, (newstr.substr(i + 1, len)));
/*表示把newstr当前标点符号位置,到最后位置,replace,替换成substr截取从标点符号
+1到结束的位置的字符串,然后再赋值给newstr*/
len--;//当它每替换一次时,len就要减1,否则长度改变了,还照旧循环,会越界
i--;//同时把i的值减1,确保读取替换过的字符是否是标点符号
}
}
cout << newstr << endl;
/*输出结果
,./hello//., world,';'123
hello world123
请按任意键继续. . .*/
system("pause");//暂停关闭窗口
return 0;
}
=====================================================================
第3章 字符串、向量和数组086页 使用vector
=====================================================================
//QQ108201645编写
#include <vector>
#include <string>
#include <iostream>
#include "Sales_item.h"
using std::cout; using std::cin; using std::endl;
using std::vector;//使用vector向量
using std::string;//使用string
int main()
{
vector<int> ivec;//ivec保存int 类型的对象
vector<Sales_item> sales_vec;//保存Sales_iter类型的对象
vector<vector<string>>file;//该向量的元素保存的是vector类型
}
=====================================================================
表3.4:vector初始化方法 | |
vector<T> v1 | v1是一个空vector,它潜在的元素是T类型,执行默认初始化 |
vector<T> v2(v1) | v2中包含v1所有的元素的副本 |
vector<T> v2=v1 | 同上 |
vector<T> v3(n,val) | v3包含了n个重复的元素,每个元素都是val |
vector<T> v4(n) | v4包含了n个重复地执行了初始化的对象 |
vectro<T> v5{a,b,c...} | v5包含了初始值个数的元素,每个元素被赋予相应的初始值 |
vectro<T> v5={a,b,c...} | 同上 |
=====================================================================
第3章 字符串、向量和数组088页 vector列表初始化
=====================================================================
//QQ108201645编写
#include <vector>
#include <string>
#include <iostream>
using std::cout; using std::cin; using std::endl;
using std::vector;//使用vector向量
using std::string;//使用string
int main()
{
//vector<string> svec;//默认初始化,svec不含任何元素
vector<int> ivec;//初始状态为空
vector<int> ivec2(ivec);//把ivec的元素拷贝给ivec2
vector<int> ivec3=ivec;//把ivec的元素拷贝给ivec3
//vector<string> svec(ivec2);//错误:svec的元素是string对象,不是int
//列表初始化
vector<string> articles = { "a","an","the" };//包含string对象的三个元素(每个都是字符串)
vector<string> v1{ "a", "an", "the" };//列表初始化
//vector<string> v2( "a", "an", "the" );//错误,不能用小括号
return 0;
}
=====================================================================
第3章 字符串、向量和数组088-089页 vector默认初始化()与列表初始化{}
=====================================================================
//QQ108201645编写
#include <vector>
#include <string>
#include <iostream>
using std::cout; using std::cin; using std::endl;
using std::vector;//使用vector向量
using std::string;//使用string
int main()
{
vector<int> ivec(10, -1);//10个int类型的元素(默认初始化)
for (auto c:ivec)//自动推导类型进行范围for循环
{
cout << c << "\t";//输出10个-1
}
vector<string> svec(10, "hi");//包含10个string 类型的元素,每个初始化为hi
vector<int> ivec1(10);//10个元素,每个默认为0
vector<string> svec1(10);//10个默认为空的string对象
//vector<int> vi = 10;//错误,必须直接初始化的形式指定向量大小
vector<int> vi{ 1,2,3,4,5 };//列表初始化
vector<int> v1(10);//v1有10个元素,默认为0
vector<int> v2{ 10 };//v2有一个元素,无素的值是10(列表初始化)
vector<int> v3(10, 1);//v3有10个元素,默认为1
vector<int> v4{ 10,1 };//v4有2个元素,两个元素的值分别是10与1
//当无法执行列表初始化时,编译器会用默认初始化vector
vector<string> v5{ "hi" };//列表初始化:v5持有一个元素
//vector<string> v6("hi");//错误,不能使用字符串字面值构建vector对象
vector<string> v7{ 10 };//v7有10个默认初始化的元素,等同于vector<string> v7(10);
vector<string> v8{ 10,"hi" };//v8有10个值为"hi"的元素,等同于vector<string> v8(10,"hi");
//练习题3.12下列的vector定义有不正确吗?如果有请指出来对于正确的请描述执行结果
vector<vector<int>>ivec2;//正确,该向量的元素是vector
//vector<string> svec2 = ivec;//错误,svec2的元素是string,不是vector
vector<string> svec3(10, "null");//正确,默认初始化10个素,每个都是字符串"null"
//练习题3.13下列vector对象是包含多少个元素,值又分别是多少
vector<int> vr1;//包含一个元素,值默认为0
vector<int> vr2(10);//包含10个元素,每个值默认为0
vector<int> vr3(10, 42);//包含10个元素,每个值初始为42
vector<int> vr4{ 10 };//包括一个元素,值为10
vector<int> vr5{ 10,42 };//包含两个元素,值为10与42
vector<string> vr6{ 10 };//包含10为空串的元素
vector<string> vr7{ 10,"hi" };//包含10为"hi"字符串的元素
return 0;
}
=====================================================================
第3章 字符串、向量和数组090页 向vector对象添加元素
=====================================================================
//QQ108201645编写
#include <vector>//包含头文件vector
#include <string>//包含头文件string
#include <iostream>
using std::cout; using std::cin; using std::endl;
using std::vector;//使用vector向量
using std::string;//使用string
int main()
{
vector<int> v2;//空vector对象
for (int i=0;i!=100;++i)
{
v2.push_back(i);//依次把i的值放到v2尾端
}
//for循环结束后v2有100个元素,值从0到99
string word;
vector<string> test;
while (cin >> word)//用ctrl+z回车结束
{
test.push_back(word);
}
return 0;
}
=====================================================================
表3.5:vector支持的操作 | |
v.size() | 如果v不含有任何元素,返回真,否则返回假 |
v.empty() | 返回v中元素的个数 |
v.push_back(t) | 向v的尾端添加一个值为t的元素 |
v[n] | 返回v的第n个位置上元素的引用 |
v1=v2 | 用v2中元素,拷贝替换v1的元素 |
v1={a,b,c...} | 用列表中的元素替换v中的元素 |
v1==v2 | v1和v2相等,并且当它们的元素数量相同且对应位置的元素值都相同 |
v1!=v2 | v1和v2不相等 |
<,<=,>,>= | 顾名思义,以字典顺序进行比较 |
=====================================================================
第3章 字符串、向量和数组092页 其它vector操作
=====================================================================
//QQ108201645编写
#include <vector>//包含头文件vector
#include <string>//包含头文件string
#include <iostream>
using std::cout; using std::cin; using std::endl;
using std::vector;//使用vector向量
using std::string;//使用string
int main()
{
vector<int> v{ 1,2,3,4,5,6,7,8,9 },v1(v);//列表初始化9个元素
int cmp = (v.size() == v1.size());
cout <<"两个长度一样返回"<< cmp << endl;
for (auto &i : v)//对于v中每个元素(注意是引用)
i *= i;//求元素值的平方,i=i*i
for (auto i : v)//对于v中的每个元素
cout << i << " ";//输出v中的每个元素
cout << endl;//输出换行
//输出结果:1 4 9 16 25 36 49 64 81
vector<int>::size_type sp;//正确
//vector::size_type sp1;//错误
/*v.size()的使用:如果两个vector对象小于元素比较多的vector对象
若元素的值有区别,则vector对象的大小关系由第一对相异的元素值的大小关系决定*/
system("pause");
return 0;
}
=====================================================================
#include <vector>//包含头文件vector
#include <iostream>
using std::cout; using std::cin; using std::endl;
using std::vector;//使用vector向量
int main()
{
vector<unsigned>scores;
vector<unsigned>grade(11, 0);//初始(等级)11个为0的元素
unsigned score;//定义成绩分数
while (cin >> score)
{
if (score <= 100)//处理有效的成绩ctrl+z回车结束
{
scores.push_back(score);
++grade[score / 10];//将对应分数段加1
/*等价于
auto ind = score/10;//得到分数段索引
++grade[ind];//将位置计数值加1
*/
}
}
for (auto g : grade)
{
cout << g << " ";
}
cout << endl;
for (auto it = scores.begin(); it != scores.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
/*
输出
42 65 95 100 39 67 95 76 88 76 83 92 76 93^Z
0 0 0 1 1 0 2 3 2 4 1
42 65 95 100 39 67 95 76 88 76 83 92 76 93*/
/*
//不能用下标形式添加元素
vector<int> ivec;//空vector对象
for(decltype(ivecc.size()) ix=0;ix!=10;++ix)
ivec[ix]=ix;//严重错误,ivec不包含任何元素
这段代码是一个空的vector,不包含任何元素,不能通过下标访问任何元素
正确的方法是ivec.push_back(ix);
下标只必须是只能对已知存在的元素进行操作
vector<int> ivec;//空vector对象
cout<<ivec[0];//错误,未包含任何元素
vector<int> ivec2(10);//含有10个元素的vector对象
cout<<ivec2[10];//错误:ivec2元素检索引只有0~9
试图用下标去访问一个不存在的元素将引发错误。且不被编译器发现,
而是产生一个不可预知的值
*/
system("pause");
return 0;
}
=====================================================================
第3章 字符串、向量和数组094页 vector练习题(抽选题)
=====================================================================
//QQ108201645编写
#include <vector>//包含头文件vector
#include <string>
#include <iostream>
using std::cout; using std::cin; using std::endl;
using std::vector;//使用vector向量
using std::string;
typedef vector<int> INTVEC;//重定义vector<int> 类型名
typedef vector<string> STRVEC;//重定义vector<string>类型名
void ShowIntVec(INTVEC& v)
{
cout << "大小:"<<v.size() << "个元素 返回0表示空:" << !v.empty() << endl;//输出大小
if (!v.empty())//判断是否不是空,空的话返回true,所以加上!返回false
{
for (auto c : v)
{
cout << c << " ";
}
}
cout << endl;
}
void ShowStrVec(STRVEC& v)//由函数输出
{
cout << "大小:" << v.size() << "个元素 返回0表示空:" << !v.empty() << endl;//输出大小
if (!v.empty())//判断是否不是空,空的话返回true,所以加上!返回false
{
for (auto c : v)
{
cout << c << " ";
}
}
cout << endl;
}
int main()
{
//练习3.16编写一段程序,把3.13中vector对象容量和具体内容输出
//验证是否对,不对请重新学习3.3.1节(87页)
INTVEC v1;//默认为空的int类型的vector对象
cout << "输出v1的大小,如果是空则不输出内容" << endl;
ShowIntVec(v1);
INTVEC v2(10);//默认每个元素初始化值0,有10个元素的int类型的vector对象
cout << "输出v2的大小,内容" << endl;
ShowIntVec(v2);
INTVEC v3(10, 42);//默认每个元素初始化值42,有10个元素的int类型的vector对象
cout << "输出v3的大小,内容" << endl;
ShowIntVec(v3);//对应含有int类型的vector对象的函数
INTVEC v4{ 10 };//列表初始化值,一个元素值为10
cout << "输出v4的大小,内容" << endl;
ShowIntVec(v4);
INTVEC v5{ 10,42 };//列表初始化值,2个元素值为10,42
cout << "输出v5的大小,内容" << endl;
ShowIntVec(v5);
STRVEC v6{ 10 };//默认初始化10个元素的vector对象,每个元素初始字符串为空
cout << "输出v6的大小,内容" << endl;
ShowStrVec(v6);//调用对应的含有string类型的vector的对象的函数
STRVEC v7{ 10,"hi" };//默认初始化10个元素的vector对象,每个元素初始字符串为"hi"
cout << "输出v7的大小,内容" << endl;
ShowStrVec(v7);
system("pause");
return 0;
}
=====================================================================
//练习3.17,大写转小写,小写转大写
//QQ108201645编写
#include <vector>//包含vector头文件
#include <cctype>//包含cctype头文件
#include <string>//包含string头文件
#include <iostream>
using std::cout; using std::cin; using std::endl;
using std::vector;//使用vector向量
using std::string; using std::getline;
using std::tolower; using std::toupper; using std::islower; using std::isupper;
int main()
{
string buf;
vector<string> svec;
while (getline(cin,buf)&&buf!="end")//循环接收词,直到遇到输入"end"结束
{
svec.push_back(buf);
}
for (auto &s:svec)//自动推导成vector类型
{//每次输出的类型就是string,所以继续用范围for循环推导
for (auto &c:s)//自动推导成string类型
{
if (islower(c))//判断单个字符是不是小写
{
c = toupper(c);//是小写的话转成大写
}
else if(isupper(c))//判断单个字符是不是大写
{
c = tolower(c);//是大写的话转成小写
}
}
cout<<"输出:"<<s<<endl;
}
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <vector>//包含vector头文件
#include <iostream>
using std::cout; using std::cin; using std::endl;
using std::vector;//使用vector向量
int main()
{
//练习题3.19
//vector<int> ivec;//不合法,没有元素
//改成下面
vector<int> ivec{ 1 };//包含一个元素,这个元素的值为1
ivec[0] = 42;
cout << ivec[0] << endl;//输出这个元素
//练习题3.20读入一组整数存到vector,将每对相邻的整数的和输出。
vector<int> ivec1;
unsigned num=0;
auto i = num;
while (cin>> num)//输入一组数ctrl+z回车结束
{
ivec1.push_back(num);
}
for (i=0;i<ivec1.size()-1;i++)
{
if (i%2==0)//能被二整除的数
{
cout << ivec1[i] + ivec1[i + 1] << " ";//输出每对的和
}
}
cout<<endl;
//改写程序,这次要求先输出第1个和最后一个元素的和,接着是第二个和倒数第二个元素的和
//依此类推<只修改一下算法>
auto j = ivec1.size() - 1;
for (i=0;i<ivec1.size()/2;++i,--j)//j等于最后一次,每次减一,i+1直到一半
{
cout << ivec1[i] + ivec1[j] << " ";//头尾相加
}
cout << endl;
system("pause");
return 0;
}
=====================================================================
第3章 字符串、向量和数组096页 vector迭代器
=====================================================================
表3.6:标准容器迭代器的运算符 | |
*iter | 返回迭代器iter所指元素的引用 |
iter->mem | 解引用iter并获取该元素名为mem的成员,等价于(*iter).mem |
++iter | 令item指示容器中的下一个元素 |
--iter | 令item指示容器中的上一个元素 |
iter1==iter2 | 判断两个迭代器是否相等(或不相等),如果两个迭代器指示的是同一个元素或者它们是同一个容器的尾后迭代器,则相等;反之,不相等 |
iter1!=iter2 |
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>//包含string头文件
#include <cctype>//包含cctype头文件
using std::cout; using std::cin; using std::endl;
using std::string;
using std::toupper; using std::isspace;
int main()
{
string s("some string");
if (s.begin() != s.end())//确保s非空,检查第一位是否是结尾
{
auto it = s.begin();//it表示s的第一个字符
*it = toupper(*it);//将当前字符改成大写形式
}
//依次处理s的字符直到结束或遇到空白字符
for (auto it = s.begin(); it !=s.end()&&!isspace(*it); ++it)
{//用s.begin()初始化it,并传值给it
*it = toupper(*it);//将当前字符改大写
}
cout<<s<<endl;
system("pause");//暂停在控制台,直到按回车关闭
return 0;
}
=====================================================================
第3章 字符串、向量和数组097~99页 vector迭代器类型
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>//包含string头文件
#include <cctype>//包含cctype头文件
#include <vector>
using std::cout; using std::cin; using std::endl;
using std::string; using std::vector;
using std::toupper; using std::isspace;
int main()
{
#ifdef LIST_INIT
vector<int>::iterator it;//it能读写vector<int>的元素
string::iterator it2;//it2能读写string对象中的字符
vector<int>::const_iterator it3;//it3只能读元素,不能写元素
vector<string>::const_iterator it4;//it4只能读字符,不能写字符
#else
vector<int> v;
#endif
const vector<int> cv;
auto it1 = v.begin();//it1的类型是vector<int>::iterator
auto it2 = cv.begin();//it2的类型是vector<int>::const_iterator
auto it3 = v.cbegin();//it3的类型是vector<int>::const_iterator
//C++11新标准加入了两个新函数,分别是cbegin(),与cend,类似begin()与end()
//也是分别返回指示容器的第一个或最后元素下一位置,不同的是,不论是vector
//还是string对象,本身是否常量,返回值都是const_iterator
vector<string> text{ 5,"abc" };
auto it = text.begin();
cout << (*it).empty() << endl;//解引用it,然后调用结果对象的empty成员
#ifdef LIST_INIT
*it->empty();//错误,试图访问it的名为empty的成员,但it是个迭代器,没有empty成员
#endif
cout << it->empty() << endl;//与(*it).empty()相同
for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
{//初始化it指向text第一个元素,直到结束或遇到空。cbegin()与cend()只能用来读取不能写入
cout << *it << " ";
}
cout << endl;
//已知一个问题不能在for循环中向vector对象添加元素。
//另外一个限制是任何一种可能改变vector容量的操作,如push_back,都会
//使该迭代器失效(315页将详细将解)
#ifdef LIST_INIT
//复习部分
vector<int>ivec;//空对象
cout << ivec;//错误;不包含任何元素
vector<int> ivec2;//含有10个元素的vector对象
cout << ivec2[10];//错误;ivec2元素的合法索引是从0~9
#endif
//练习3.22把text第一段的程序,全部改大写
for (auto it = text.begin(); it != text.end() && !(*it).empty(); ++it)
{
for (auto &c : *it)
{
c = toupper(c);
}
cout << *it << " ";
}
cout << endl;
//练习3.23:编写一段程序,创建一个含有10个整数的vector对象,用迭代器
//将所有元素变成原来的两倍
vector<int> invec{ 1,2,3,4,5,6,7,8,9,10 };
for (vector<int>::iterator it = invec.begin(); it != invec.end(); ++it)
{
*it *= 2;
cout << *it << " ";
}
cout << endl;
system("pause");//暂停在控制台,直到按回车关闭
return 0;
}
=====================================================================
表3.7:vector和string迭代器支持的运算 | |
iter + n | 迭代器加一个整数值仍得到一个迭代器,迭代器指示的新位置与原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置 |
iter - n | 迭代器减去一个整数值仍得到一个迭代器,迭代器指示的新位置与原来相比向后移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置 |
iter1 += n | 迭代器加法的复合赋值语句,将iter1加n的结果赋给iter1 |
iter1 -= n | 迭代器加法的复合赋值语句,将iter1减n的结果赋给iter1 |
iter1 – iter2 | 两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后将得到左侧的迭代器。参与运算的两个迭代器必须向的是同一个容器中的元素或者尾元素的下一位置 |
>、>=、<、<= | 迭代器的关系运算符,如果迭代器指向的容器位置在另一个迭代器所指位置之前,则说前者小于后者。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置 |
=====================================================================
第3章 字符串、向量和数组100页 vector的算术运算
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>//包含string头文件
#include <vector>
using std::cout; using std::cin; using std::endl;
using std::string; using std::vector;
int main()
{
#ifdef LIST_INT//防止变量名冲突
vector<string> vi(20,"123");
auto mid = vi.begin() + vi.size() / 2;//计算得到最接近vi中间元素的一个迭代器
cout<<"mid的位置:"<<vi.end()-mid<<endl;
vector<string>::iterator it = vi.begin();
if (it<mid)
{
cout<<"it<mid"<<endl;
}
#endif
//二分搜索
string buf = "000";
vector<string> text;
//初始26个字母的字符串
for (int i=0;i<10;++i)
{
text.push_back(buf);
for (auto &c:buf)
{
++c;
}
}
for (auto c:text)
{
cout<<c<<endl;
}
//书中代码
auto beg = text.begin(), end = text.end();
auto mid = text.begin() + (end - beg) / 2;//初始状态下的中间点
//当还有元素尚未检查并且我们还没找到sought时执行检查
string sought = "777";
while (mid!=end&&*mid!=sought)
{
if (sought < *mid)//我们要的元素在前半部吗
end = mid;//如果是,调整搜索范围使得忽略掉后半部分
else//我们要的元素在后半部吗
beg = mid + 1;//在mid之后寻找
mid = beg + (end-beg)/ 2;//新的中间点
}
cout <<"位置:"<< mid-text.begin()<< " = " << *mid << endl;
for (auto c:text)
{
cout<<c<<endl;
}
system("pause");//暂停在控制台,直到按回车关闭
return 0;
}
=====================================================================
第3章 增加内容,输入字符串,二分法查找每个字符所在的位置
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>//包含string头文件
#include <vector>
using std::cout; using std::cin; using std::endl;
using std::string; using std::vector;
int main()
{//查找每个字符所在位置
//初始化26个元素的vector,并打印输出
vector<char> strVec;
char str = 'A';
cout << "输出:" << endl;
while (str != 'z' + 1)//如果字符不等于z,则循环
{
strVec.push_back(str);
str == 'Z' ? str = 'a' : str++;
//是不是大写的Z,是的话把a字符传给str,如果不是则继续加1操作
}
for (auto Show : strVec)
{
cout << Show << ((Show == 'Z') ? "\n" : " ");//当它等于大写Z则换行,否则输出空格
}
cout << "\n 输入一组字符串,分别标出它的位置" << endl;
string buf;
cin >> buf;
for (auto sought : buf)
{
auto beg = strVec.begin(), end = strVec.end();
auto mid = strVec.begin() + (end - beg) / 2;
while (mid != end && *mid != sought)
{
if (sought < *mid)
end = mid;
else
{
beg = mid + 1;
}
mid = beg + (end - beg) / 2;
}
cout << mid - strVec.begin() + 1 << " " << *mid << endl;
}
system("pause");//暂停在控制台,直到按回车关闭
return 0;
}
=====================================================================
第3章 字符串、向量和数组101页 vector练习
=====================================================================
//练习3.24
#include <iostream>
#include <vector>
using namespace std;//使用命名空间
typedef vector<unsigned> INTVEC;//重定义类形名
int main()
{
INTVEC score;
unsigned num;
while (cin>>num)//输入完,ctrl+z回车结
{
score.push_back(num);
}
//处理每对相加
for (INTVEC::const_iterator it=score.begin();
it!=score.end()&&score.end()-it>1;it+=2)//大于1防止输入位数是单数的错误
{
cout << *it + *(it + 1) << " ";
}
cout << endl;
INTVEC::const_iterator beg = score.begin();
INTVEC::const_iterator end =beg+ (score.end()-beg)-1;//一半防止输入位数是单数的错误
while (beg<end)
{
cout << *beg + *end << " ";
beg++, end--;
}
cout << endl;
system("pause");//暂停
return 0;
}
=====================================================================
//练习3.25
//QQ108201645编写
#include <iostream>
#include <vector>
using namespace std;//使用命名空间
int main()
{
vector<unsigned> score;
vector<unsigned> grade(11, 0);
/*默认11个分数(1)0~9、(2)10~19、(3)20~29、(4)30~39、(5)40~49、(6)50~59、
(7)60~69、(8)70~79、(9)80~89、(10)90~99、(11)100 */
unsigned num = 0;
while (cin>>num)
{
if (num <= 100)
{
vector<unsigned>::iterator c = grade.begin();//c初始是首位
score.push_back(num);
++(*(c + num / 10));//把c加上num除10的位置的值进行++;
}
}
for (vector<unsigned> ::const_iterator gradeit=grade.begin();
gradeit!=grade.end();++gradeit)
{
cout << *gradeit << " ";
}
cout << endl;
system("pause");//暂停
return 0;
}
/*
练习题3.26,两个都是迭代器是不能相加
*/
=====================================================================
第3章 字符串、向量和数组101页 数组
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;//使用命名空间
constexpr int get_size()
{
return 10;//返回一个10
}
int main()
{
unsigned cnt = 42;//不是常量表达式
constexpr unsigned sz = 42;//常量表达式,关于constexpr参见,2.44节(第59页)
int arr[10] = { 0 };//包含十个元素的数组
int *parr[sz];//包含42个整型指针的数组
//string bad[cnt];//错误;cnt不是常量表达式
string strr[get_size()];//当get_size()返回类型是constepxr时正确,否则错误
system("pause");//暂停
return 0;
}
/*定义数组时必须指定数组的类型,不允许用auto关键字由初始值的列表推断
类型。另外和vector一样,数组的元素应为对象,因为不存在引用数组*/
/*如果在声明时没有指明维度,编译器会根据初始值的数量计算并推测出来,
相反,如果指明了维度,那么初始值不应该超出指定的大小,如果维度比提供的
初始值数量大,则用提供的初始值初始化靠前的元素,剩下的元素被初始化成默认值
*/
=====================================================================
第3章 字符串、向量和数组101~103页 数组的定义与初始化
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;//使用命名空间
int main()
{
const unsigned sz = 3;
int arr1[sz] = { 0,1,200 };//含有三个元素的数组,分别是0,1,2
int arr2[] = { 0,1,2 };//维度是3的数组
int arr3[5] = { 0,1,2 };//等价于arr3[5]={0,1,2,0,0};
string arr4[3] = { "hi","bye" };//等价于arr4[3]={"hi","bye",""};
//int arr5[2] = { 0,1,2 };//错误,初始值过多
/*另外有一种额外的初始方式,初始后字符串字面值的结尾还有一个空字符
这个空字符也会像字符串一的其它字符一样被拷贝到字符串数组中*/
char a1[] = { 'C','+','+' };//列表初始化,没有空字符
char a2[] = { 'C','+','+','\0' };//列表初始化,含有显示的空字符
char a3[] = "C++";//自动添加表示字符串结的空字符
//const char a4[6] = "Daniel";//错误,没有空间存放空字符
/*a4[6]尽管看起来只有6个字符,但数组的实际大小必须至少是7,其中6个存放字
面值的内容,另外一个存放结尾处的空白符*/
//不能将数组赋值给其它内容作为初始值
int array[] = { 0,1,2 };//含有三个整数的数组
//int array1[] = array;//错误,不允许使用一个数组初始化另一个数组
//array1 = array;//错误。不能把一个数组直接赋值给另一个数组
int *ptrs[10];//ptrs是含有10个整形指针的数组
// int &refs[10] =/*?*/;//错误,不存在引用的数组
int arr[10] = { 0 };
int(*Parray)[10] = &arr;//Parray指向一个含有10个整数的指针
//*Parray是个指向int类型并且含有10个元素的数组的指针,
int(&arrRef)[10] = arr;//arrRef引用一个含有10个整数的数组
//&arrRef表示是一个引用的对象是一个含有大小为10的int类型的数组
int *(&arry)[10] = ptrs;//arry是数组的引用,该数组含有10指针
//arry就是一个含有10个int型指针的数组的引用
system("pause");//暂停
return 0;
}
=====================================================================
第3章 字符串、向量和数组104页 访问数组元素
=====================================================================
//QQ108201645编写
#include <iostream>
#include <cstddef>
#include <string>
using namespace std;//使用命名空间
int main()
{
/*size_t是unsigned int类型,它被设计的足够大以便能表示内存中的任意对象
在cstddef头文件中定义了这个类型,这个文件是c标准库stddef.h头文件的C++语言版本*/
//数组除了大小固定这一特点外,其它用法与vector基本类似
//如,可以用数组来记录各个分数段的成绩个数
unsigned grade[11] = {};//11个分数段,初始为0
unsigned scores;
while (cin>>scores)//接收分数
{
if (scores<=100)
{
++grade[scores / 10];//将当前分段数计数+1
}
}
for (int i=0;i<11;i++)//人为控制遍历
/*或者使用for(auto i:grade)
cout<<i<<" ";
cout<<endl;
*/
{
cout << grade[i] << " ";
}
cout << endl;
system("pause");//暂停
return 0;
}
=====================================================================
第3章 字符串、向量和数组104页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cstddef>
#include <vector>
using namespace std;//使用命名空间
int main()
{//练习题3.30
constexpr size_t array_size = 10;
int ia[array_size];
#ifdef ERROR
/*size_t是unsigned int类型,它被设计的足够大以便能表示内存中的任意对象
在cstddef头文件中定义了这个类型,这个文件是c标准库stddef.h头文件的C++语言版本*/
for (size_t ix = 1; ix <= array_size; ++ix)
{//错误,数组的首位置是ia[0]到ia[9],当ix加到==array_size时,就是ix==10,越界
ia[ix] = ix;
}
#endif
//修改如下
cout << "输出含有10个int的数组,令每个元素就是下标值" << endl;
for (size_t ix = 0; ix < array_size; ++ix)//当是10时就就退出循环
{
ia[ix] = ix + 1;//加1方便查看,ix本身值没有改变,只有ix+=1,才会改变
cout << ia[ix] << " ";//循环输出当前值1~10
}
//练习题3.31同上
//练习题3.32将上一题创建的数组拷贝给另一个数组。
cout << "\n将上一题创建的数组拷贝给另一个数组" << endl;
int ib[array_size];
auto i = 0;
for (auto &c : ib)
{
c = ia[i++];
}
for (auto &c : ib)
{
cout << c << " ";
}
cout << "\n利用vector重写实现类似的功能" << endl;
vector<unsigned> ic;
for (auto ix : ib)
{
ic.push_back(ix);//把ix的元素依将存入vector向量中
}
for (auto ix : ic)
{
cout << ix << " ";
}
cout << endl;
system("pause");//暂停
return 0;
}
=====================================================================
第3章 字符串、向量和数组105~106页 指针与数组
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <iterator>
using namespace std;//使用命名空间
int main()
{
//对数组使用下标运算符得到该数组的指定位置的元素,像其它对象一样。
//对数组元素使用取地址符就能得到该元素的指针
string nums[] = { "one","two","three" };//数组的元素是string对象
string *p = &nums[0];//p指向数组的第一个元素
//在很多用到数组名的地方,编译器会自动将其替换成指向数组首元素的指针,
string *p2 = nums;//等价于p2=&nums[0];
int ia[] = { 0,1,2,3,4,5,6,7,8,9 };//ia是一个含有10个整数的数组
auto ia2(ia);//等价于auto ia2(&ia[0],ia2是一个int*整型指针,指向ia数组的第一个元素
//ia2 = 42;//错误,ia2是一个指针,不能用int值赋给指针
//大多数类型,指向数组对象其实是使用一个指向数组首元素,首位置的指针
decltype(ia) ia3 = { 0,1,2,3,4,5,6,7,8,9 };
//ia3 = p;//错误,不能用整型指会给数组赋值
int i = 4;
ia3[4] = i;//正确,把i的值赋给ia3的一个元素
//指针也是一个迭代器
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
int *p1 = arr;//p1指向arr的第一个元素
++p1;//p1地址++,p1指向arr[1]
//就像使用迭代器遍历vector的元素一样,使用指针也能遍历数组中的元素
//前提是先获取到指向数组的第一个元素的指针和指向数组尾元素的不止一位置的指针。
//不过获取尾后指针就用到数组的特殊性质,可以获取尾元素之后那个并不存在的元素地址
int *e = &arr[10];//指向arr尾元素的下一位置的指针,但实际不存在
//0~9尾元素的位置是9,接下来那个不存在的元素唯一的用处就是提供指针初始化e
//就像尾迭代器,尾后指针也不指向具体元素,因为不能对尾后指针进行解引用或递增操作
for (int *b = arr; b != e; ++b)//三个内容,b指向arr首位置。当b不等于10。++b
cout << *b << endl;//输出arr的元素
/*C++11新标准引入了begin与end函数,这两个函数与容器中的两个同名成员,功能类似
不过数组毕竟不是类类型,因为这两个函数不是成员函数,正确的使用是将数组作为它们的参数
begin()函数返回指向数组首位置的指针,end()函数返回指向数组尾元素的下一个位置的指针,
这两个定义在iterator头文件中*/
int ia1[] = { 0,1,2,3,4,5,6,7,8,9 };//ia1是一个含有10个整数的数组
int *beg = begin(ia1);//指向ia1首元素的指针
int *last = end(ia1);//指向ia1尾元素的下一位置的指针
//begin函数返回指向ia1首元素的指针,end函数返回指向ia1尾元素的下一位置的指针
while (beg != last)
{
cout << *beg << " ";//循环输出内容
beg++;//地址+1
}
cout << endl;
int arr1[] = { 0,1,2,3,4,5,6,7,8,9 };
int* pbeg = begin(arr1), *pend = end(arr1);//pbeg指向数组的首位置,pend指向数组尾元素的下一位置
while (pbeg != pend && *pbeg >= 0)//当pbeg不等于pend,并且*pbeg的值大等于0时循环
{//寻找第一个负值元素,如果检查全部元素则结束循环
cout << *pbeg << " ";
++pbeg;
}
//while循环通过比较pbeg和pend来确保可心安全地对pbeg解引用,如果pbeg确实指向了一个元素,检查
//是否是负数,是的话条件失败,退出循环。如果不是,将指针向前移动继续考查下一元素,特别注意,尾后指针不能解引用和递增操作
system("pause");//暂停
return 0;
}
=====================================================================
第3章 字符串、向量和数组107页 指针运算
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cstddef>
#include <iterator>
using namespace std;//使用命名空间
int main()
{
/*size_t是unsigned int类型,它被设计的足够大以便能表示内存中的任意对象
在cstddef头文件中定义了这个类型,这个文件是c标准库stddef.h头文件的C++语言版本*/
constexpr size_t sz = 5;
int arr[sz] = { 1,2,3,4,5 };
int *ip = arr;//等价于int *ip=&arr[0]
int *ip2 = ip + 4;//等价于int *ip2=&arr[4];指向尾元素
//ip加4得到结果仍是一个指针,该指针所指的元素相比前进了4个位置。(注意不是ip+=4,所以ip地址并未改变)
int *p = arr + sz;//使用警告:不要解引用
//编译器自动将arr转换成指向数组的首位置。把这个地址赋给p指针,执行加法后。指针
//从首元素位置向前移动了5个位置,指向尾元素的下一位置,如果计算所得的指针超出了范围,会出错,但编译器是发现不了
//int *p2 = arr + 10;//错误,arr只有5个元素,p2的值未定义
//重复内容:
/*C++11新标准引入了begin与end函数,这两个函数与容器中的两个同名成员,功能类似
不过数组毕竟不是类类型,因为这两个函数不是成员函数,正确的使用是将数组作为它们的参数
begin()函数返回指向数组首位置的指针,end()函数返回指向数组尾元素的下一个位置的指针,
这两个定义在iterator头文件中*/
auto n = end(arr) - begin(arr);//n的值是5,也就是arr中元素的数量
cout << n << endl;//输出数组长度
//两个指针相减的结果是一种名为ptrdiff_t的标准库类型,和size_t一样,ptrdiff_t也是一种定义在
//cstddef头文件中的机器相关的类型,因为差值可能为负,,所以ptrdiff_t是一种带符号类型
int *b = arr, *e = arr + sz;//b指针 指向数组的首位置,e指针指向数组+5就是尾位置加1
while (b < e)
{
//使用*b
cout << *b << " ";
++b;//位置递增
}
cout<<endl;//换行
int i1 = 0, sz1 = 42;
int *p1 = &i1, *e1 = &sz1;
//未定义的:p1和e1无关,因此比较毫无意义
//解引用与指针运算的交互
while (p1 < e1);
//如果两个指针是不相关的对象,则不能比较它们
int ia[] = { 0,2,4,6,8 };//含有5个整数的数组
int last = *(ia + 4);//正确,把last初始化成8,也就是ia[4]
//表达式计算*(ia+4)计算ia前进4个元素的新地址,把值赋给last,等价于int last=ia[4];
last = *ia + 4;//正确last=4,等价于last=ia[0]+4
system("pause");//暂停
return 0;
}
=====================================================================
#include <iostream>
#include <string>
#include <cstddef>
#include <iterator>
using namespace std;//使用命名空间
int main()
{
int ia[] = { 0,2,4,6,8 };//含有5个整数的数组
int i = ia[2];//ia[2]得到(ia+2)所指元素的值
int *p = ia;//p指向ia的首地址,*p的值是首地址的值
i = *(p + 2);//等价于i=ia[2]或i=p[2];当指针指向数组时,本身也可以理解为一个数组
//只要指针指向数组中的元素,(或者数组中尾元素的下一位置),都可以执行下标(中括号)运算
int *p1 = &ia[2];//p1指向索引为2的元素
int j = p1[1];//p1[1]等价于*(p+1),就是把ia[3]表示的那个元素赋值给j,因为原先p1初始指向ia[2],所以加1是a[3]元素的值
int k = p1[-2];//p1初始指向a[2],所以p1[-2]指向的是ia[0];
//标准库类型string与vector的下标运算与数组有所不同,标准库类型限定使用下标是无符号类型,
//而内置的下标运算无此要求,内置的可以处理负值,前提是地址必须在数组元素的范围内
system("pause");//暂停
return 0;
}
=====================================================================
第3章 字符串、向量和数组108页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <iterator>
using namespace std;//使用命名空间
int main()
{
//练习题3.34(抽选写题)假设p1与p2指向同一个数组中的元素,则下面程序功能是什么,什么情况下程序是非法的
int ia[] = { 0,1,2,3,4,5 };
int* p1 = ia, *p2 = ia;//等价于int *p1=&ia[0],*p2=&ia[0]
p1+=p2-p1;//等价于p1 =p1+( p2 - p1)
//p1=p1地址加((p2-p1)的差值)如果是p1+=p2就是非法,地址不能相加
//练习题3.35编写程序,用指针将数组中的元素置0
for (p1=ia,p2=ia+5;p1!=p2;p1++)
{//p1等于ia的首地址,p2等于ia尾地址(尾元素)的下一个位置,当不相等时,地址加1
*p1 = 0;//指针地址里的值置0
cout << *p1 << " ";//打印输出指针位置的值
}
cout << endl;
//练习题3.35编写程序,比较数组是否相等,再用vector重写
int ia1[] = { 1,2,3,4,5 };
int ia2[] = { 5,4,3,2,1 };
//sizeof(ia1)得出数组的长度,除以sizeof(int)类类型所占的大小,就是数组的个数
if (sizeof(ia1)/sizeof(int)==sizeof(ia2)/sizeof(int))//判断长度是否相等
{
for (int * pp1=ia1,i=0;i<sizeof(ia1)/sizeof(int);i++)
{
if (*(pp1+i)==ia2[i])//pp1+i的地址的值与数组进行比较,这里用不同方法
{
cout << "*(" << pp1 + i << ")的元素:"
<< *(pp1 + i) << "\t与\t" << &ia2[i] << " = " << ia2[i] << "的相等" << endl;
}
else
{
cout << "*(" << pp1 + i << ")的元素:"
<< *(pp1 + i) << "\t与\t" << &ia2[i] << " = " << ia2[i] << "的不相等" << endl;
}
}
}
else
{
cout<<"长度不一样"<<endl;
}
cout << endl;
/*vector重写*/
vector<int> ivec1{ 1,2,3,4,5 };//初始有5个元素的vector对象
vector<int>ivec2{ 5,4,3,2,1 };//同上
if (ivec1.size()==ivec2.size())//长度是否一致
{
auto pbeg = ivec1.begin(), pend = ivec1.end();//类型是vector<int>::iterator
for (auto c:ivec2)//自动推导类型,进行迭代
{
if (*pbeg==c)//当*pbeg的值与c的值一样时执行输出“一样”
{
cout << "*pbeg = " << *pbeg << "\t与\t" << " c = " << c << "一样" << endl;
}
else
{
cout << "*pbeg = " << *pbeg << "\t与\t" << " c = " << c << "不一样" << endl;
}
pbeg++;//迭代器位置++
}
}
else
{
cout<<"长度不一样"<<endl;
}
system("pause");//暂停
return 0;
}
=====================================================================
第3章 字符串、向量和数组109页 C标准库String函数
表3.8:C风格字符串的函数 | |
strlen(p) | 返回p字符串的长度 |
strcmp(p1,p2) | 比较p1与p2字符串的相等性。如果p1==p2返回0。如果p1>p2返回一个正数,如果p1<p2,返回一个负值 |
strcat(p1,p2) | 将p2附加到p1之后,返回p1.(将p2连接到p1之后) |
strcpy(p1,p2) | 将p2拷贝给p1,返回p1 |
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <iterator>
#include <string.h>//基于c风格字符串的函数
using namespace std;//使用命名空间
#pragma warning(disable:4996)//忽略4996安全警告
int main()
{
char ca[] = { 'C','+','+' };//初始化不以空字符结束
cout << strlen(ca) << endl;//严重错误:ca没有以空字符结束
//strlen函数将沿着ca在内存中的位置不断向前寻找,直到遇到空字符才结束
//比较字符串
string s1 = "A string example";
string s2 = "A different string";
if (s1<s2)
{
cout<<"s1<s2"<<endl;
}
else
{
cout<<"s1>s2"<<endl;//因为长度不一样,所以调用这个
}
//C风格
const char ca1[]= "A string example";//一个string 实例//这是连续末尾带空格的字符串
const char ca2[] = "A different string";//一个 不同 的string
//if (ca1<ca2)//未定义的:试图比较两个无关地址
if (strcmp(ca1,ca2)<0)//和s1<s2效果一样
{
cout << "ca1<ca2" << endl;
}
else
{
cout << "ca1>ca2" << endl;
}
//C++风格连接两个字符串
//string largeStr = s1 + " " + s2;//largeStr初始化成s1、一个空格、和s2的连接
//同样的操作放到ca1和ca2两个数组上就会产生错误,表达式ca1+ca2试图将两个指针相加,
//显然没什么意义,肯定是非法的
//正确的方法是使用strcat函数与strcpy函数
//如果我们计算错了,largeStr的大小将引发严重错误
unsigned len = strlen(ca1) +1+ strlen(ca2)+1 ;//最后元素为一个空字符
char *largeStr = new char[len] {0};//分配一个长度为len的char内存空间
strncpy(largeStr, ca1, sizeof(ca1));//把ca1拷贝给largeStr
cout<<largeStr<<endl;
const char c[] = " ";
strcat(largeStr," ");//在largeStr的末尾加上一个空格
cout<<largeStr<<endl;
strncat(largeStr, ca2, sizeof(ca2));//把ca2边接到largeStr的后面
cout << largeStr << endl;
delete[] largeStr;//释放内存
//练习3.37下面程序是何含义,程序输出结果是什么
const char ca3[] = { 'h','e','l','l','o' };//定义一个常量的char数组,里面的值不可以改变
const char *cp = ca3;//定义一个常量char类型的指针指向数组的首地址,且不能通过指针改变数组的内容
while (*cp)//值不为空字符时循环
{
cout << *cp << endl;
cp++;
}
//3.38 两个指针是内存地址相加,没有实际上意义
//3.39编写一段程序,比较两个string对象再编写一段程序比较C风格数组
string ss1 = "abcde", ss2 = "abcd";
if (ss1==ss2)
cout<<"ss1==ss2"<<endl;
else if(ss1>ss2)
cout << "ss1>ss2" << endl;
else
cout << "ss1<ss2" << endl;
char newch1[] = { "abcdef" }, newch2[] = { "abcde" };
if (strcmp(newch1, newch2) < 0)
{
cout << "newch1<newch2" << endl;
}
//练习3.40定义两个字符串数组,并用字面值初始化它们再定义一个字符串数组存放前两个的数组连接后的结果
char ca4[] = { 'H','e','l','l','o' }, ca5[] = { 'W','o','r','l','d' };//这是末毛不带空字符的字符串
len = sizeof(ca4) + sizeof(ca5) + 2;
//如果用strlen比较会超出数组部分,所以用sizeof,长度还要加上一个空格与末尾空字符
char *largeStr1 = new char[len] {0};
strncpy(largeStr1,ca4, sizeof(ca4));
//只能用strncpy(数组1,要连接的数组2,sizeof(数组))
//ca4末尾没有空字符,所以得到长度会出错,必须指定长度用strncpy
strncat(largeStr1, " ",1);//连接1个空格字符
strncat(largeStr1, ca5, sizeof(ca5) );
// strcat(largeStr1, ca5);
cout<<largeStr1<<endl;
delete[] largeStr1;
system("pause");//暂停
return 0;
}
=====================================================================
第3章 字符串、向量和数组111页 混用string对象和C风格字符串
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <cstddef>
#include <iterator>
#include <string.h>//基于c风格字符串的函数
using namespace std;//使用命名空间
int main()
{
string s("Hello World");//s的内容是Hello World
/*允许使用空字符结束的字符数组来初始化string对象或为string对象赋值,
在string对象的加法运算中允许使用空字符结束的字符数组作为其中一个运
算对象,不能两个都是,在string对象的复合赋值运算中允许使用以空字符结束
的字符数组作为右侧运算对象*/
//char* str = s;//错误;不能用string对象初始化char*
const char *str = s.c_str();//正确
/*c_str()函数的返回值是一个C风格的字符串,返回结果是一个指向空字符结束的
字符数组,而这个数组所存的数据恰好与那个string对象一样,返回结果是const char*
从而确保我们不会改变数组的内容:如果后续的操作改变了s的值,就可能会让其返回的数组
失效,如果执行完c_str()函数后想一直都能使用其返回的
数组,最好重新拷贝一份*/
int int_arr[] = { 0,1,2,3,4,5 };
//int_arr有6个元素
/*C++11新标准引入了begin与end函数,这两个函数与容器中的两个同名成员,功能类似
不过数组毕竟不是类类型,因为这两个函数不是成员函数,正确的使用是将数组作为它们的参数
begin()函数返回指向数组首位置的指针,end()函数返回指向数组尾元素的下一个位置的指针,
这两个定义在iterator头文件中*/
vector<int> ivec(begin(int_arr), end(int_arr));//ivec中有6个应int_arr中对应元素的副本
//用于初始化vector对象的值也可能仅是数组的一部分
vector<int> subVec(int_arr + 1, int_arr + 4);//拷贝三个元素,int_arr[1],int_arr[2],int_arr[3]
//这条初始语句用3个元素创建了对象subVec,三个元素分别来自int_arr数组的int_arr[1],int_arr[2],int_arr[3]
//112页练习题3.41编写用整形数组初始化一个vector对象
//练习题3.42编写含有整数元素的vector对象拷贝给一个整型数组
int int_array[] = { 1,2,3,4,5,6,7,8,9,10 };
vector<int> addVec(begin(int_array), end(int_array));
int array_[10];
auto sz = begin(array_);
//auto pbeg = begin(array_), pend = end(array_);
for (auto c:addVec)
{
cout << c << " ";
*sz++ = c;
}
cout << endl;
for (auto c:array_)
{
cout << c << " ";
}
cout << endl;
system("pause");
return 0;
}
=====================================================================
第3章 字符串、向量和数组112页 多维数组
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <cstddef>
#include <iterator>
#include <string.h>//基于c风格字符串的函数
using namespace std;//使用命名空间
#ifndef ARRAY
#define ARRAY
#endif
int main()
{
#ifndef ARRAY//防止数组名冲突,照书上打出
int ia[3][4];//二维数组,大小为3的数组,每个元素包含有4个整数的数组
//分别是ia[0][0],ia[0][1],ia[0][2],ia[0][3],是ia[1][0],ia[1][1],ia[1][2],ia[1][3],是ia[2][0],ia[2][1],ia[2][2],ia[2][3]
#endif
int arr[10][20][30] = { 0 };//三维数组,大小为10的数组,每个元素都是大小为20的数组,这些数组的元素含有30个整数
//第一维称做行,第二维度称作列
int ia[3][4] = {//三个元素,每个都是大小为4的数组
{0,1,2,3},//第1行的初始值
{4,5,6,7},//第2行的初始值
{8,9,10,11}//第3行的初始值
};
//与上面这条一样
int ia1[3][4] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
int ia2[3][4] = { {0},{4},{8} };//显示初始化每行首元素,其它初始为0
ia[2][3] = arr[0][0][0];//用arr的首元素为ia的最后一行最后一个元素赋值
//ia[2]得到ia数组最后一行首位置,再对这一维数组下标得到编号为3的元素,也就是最后一个元素
//类似的第一层是0最外层的数组,再对应每个大小为20的(多维)数组首位置,最后再取中20个位置中第0个的30个元素中的第一个元素
int(&row)[4] = ia[1];//把row绑定到ia的第二个值为4的元素数组上
//把row定义成含有4个整数的数组引用,然后绑定到多维数组的第2行
constexpr size_t rowCnt = 3, colCnt = 4;
int ia3[rowCnt][colCnt];//示初始化的12个元素
for (size_t i = 0; i != rowCnt; ++i)//对于每一行
{
for (size_t j = 0; j != colCnt; ++j)//对于每一列
{
ia3[i][j] = i * colCnt + j;//将元素的位置作为它的值
cout << ia3[i][j] << " ";
}
cout << endl;
}
//用auto来写,区别是使用范围for语句把管理数组的索引交给系统来完成
//要改变元素的值,所以把控制变量的row和col声明成引用类型,第一个
//for循环遍历ia所有大小为4的元素的数组,因为row的类型就是含有4个整
//数的引用,第二个for循环遍历4元素数组中的某一个,因此col的类型是整
//数的引用,每次迭代把cnt的值赋给ia的当前素。然后cnt加1
size_t cnt = 0;
for (auto &row : ia)//对于外层数组的每一个元素
{
for (auto& col : row)//对于内层数组的每一个元素
{
col = cnt;//将值赋值给该元素
++cnt;//将cnt加1
cout << col << " ";//输出
}
cout << endl;//每行加一个换行
}
//如果没有任何操作还是必须将外循环控制成引用,为了避免数组被自动转成指针
//如果row不是引用,编译器将无法通过,会将row自动转换成指向该数组内首地址的指针
//得到row的类型就是int*,显示内层的循环就不合法了,那是int*内遍历,这显然和程序的初衷不一样
cnt = 1;
for (auto &row : ia)
{
for (auto col : row)//要使用范围for,除最内层外,其它所有循环控制变量都应该是引用
{
col = cnt;//将值赋值给该元素
++cnt;//将cnt加1
cout << col << " ";//输出
}
cout << endl;//每行加一个换行
}
system("pause");
return 0;
}
=====================================================================
第3章 字符串、向量和数组115~116页 指针与多维数组
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;//使用命名空间
int main()
{
int ia[3][4] = {//三个元素,每个都是大小为4的数组
{0,1,2,3},//第1行的初始值
{4,5,6,7},//第2行的初始值
{8,9,10,11}//第3行的初始值
};
;//大小为3并且每个里面含有4个整数的数组
int(*p)[4] = ia;//p指向含有4个整数的数组
p = &ia[2];//p指向ia的尾元素
//(*p)的p是一个指针,右边是维度为4的数组,左边得知元素类型为整数,因此p是指向含有4个整数的指针
//int *ip[4];//整型指针的数数
//int (*ip)[4];//指向含有4个整数的数组
//输出ia中的每个元素的值,每个内层数组各占一行
for (auto p=ia;p!=ia+3;++p) //p指向含有4个整数的数组
{
for (auto q = *p; q != *p + 4; ++q) //q指向4个整数数组的首元素,也就是说q指向一个整数
cout << *q << ' ';
cout<<endl;
}
cout << "范围for语句输出" << endl;
for (auto p = begin(ia); p != end(ia); ++p) //p指向ia的第一个数组
{
for (auto q = begin(*p); q != end(*p); ++q) //q指向内层数组的首元素
cout << *q << ' ';//输出q所指的值
cout << endl;
}
int ia1[3][4] = {//三个元素,每个都是大小为4的数组
{100,200,300,400},//第1行的初始值
{500,600,700,800},//第2行的初始值
{900,1000,1100,1200}//第3行的初始值
};
cout << "还原指针输出" << endl;
for (int(*p)[4] = ia1; p != ia1 + 3; ++p) //p指向含有4个整数的数组
{
for (int *q = *p; q < *p+4; ++q) //q指向4个整数数组的首元素,也就是说q指向一个整数
cout << *q << ' ' << p << "\t";
cout << endl;
}
cout<<"类型别名简化的多维数组指针"<<endl;
using int_array = int[4];//新标准下类型别名的声明
typedef int int_array[4];//等价于typedef声明。参见2.51节(第60页)
for (int_array *p=ia;p!=ia+3;++p)
{
for (int *q=*p;q!=*p+4;++q)
{
cout << *q << ' ';
}
cout << endl;
}
//程序将4个整数组成的数组命名为int_array,用类型名int_array定义外层循环的控制变量,让程序显的简洁明了
system("pause");
return 0;
}
=====================================================================
第3章 字符串、向量和数组116页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;//使用命名空间
int main()
{
/*练习题3.43编写3个不同版本的程序,使其能输出ia的元素,版本1,使用范围for语句
版本2:和版本3都要使用普通的for语句,其中版本2要求用下标运算,版本3用指针,此外
在3个版本的程序中都要直接写出数据类型,而不能使用类型别名、auto关键字或decltype*/
int ia[3][4] = {
{100,200,300,400},
{400,500,600,700},
{700,800,900,1000}
};
for (int (&i)[4]:ia)//注意是引用
{
for (int j : i)
cout << j << " ";
cout << endl;
}
cout<<"/--------------/"<<endl;
for (int i=0;i<3;i++)
{
for (int j = 0; j < 4; j++)
cout << ia[i][j] << " ";
cout<<endl;
}
cout << "/--------------/" << endl;
for (int (*i)[4]=ia;i!=ia+3;++i)//i指向含有4个整数的数组
{
for (int *j = *i; j != *i + 4; ++j) //j指向4个整数数组的首元素,也就是说j指向一个整数
cout << *j << " ";
cout<<endl;
}
cout << "/练习3.44改写上一个程序,使用类型别名来代替控制变量的类型/" << endl;
using int_array = int[4];
typedef int int_array[4];
for (int_array &i : ia)
{
for (int j : i)
cout << j << " ";
cout<<endl;
}
cout << "/--------------/" << endl;
for (int_array* p=ia;p!=ia+3;++p)
{
for (int *q = *p; q != *p + 4; ++q)
cout << *q << " ";
cout<<endl;
}
cout << "/练习3.45使用auto关键字/" << endl;
for (auto &i : ia)
{
for (auto j : i)
cout << j << " ";
cout<<endl;
}
cout << "/--------------/" << endl;
for (auto i = ia; i != ia + 3; ++i)//i指向含有4个整数的数组
{
for (auto j = *i; j != *i + 4; ++j)//j指向4个整数数组的首元素,也就是说j指向一个整数
cout << *j << " ";
cout<<endl;
}
system("pause");
return 0;
}