C++Primer中文版第五版答案全集持续更新
- 第1章 开始
- 1.1 编写一个简单的c++程序
- 1.2 初识输入输出
- 1.3 注释简介
- 1.4 控制流
- 1.9 编写程序,使用while循环将50到100的整数相加
- 1.10 除了++运算符将运算对象的值增加1之外,还有一个递减运算符(--)实现将值减少1.编写程序,使用递减运算符在循环中按递减顺序打印出10到0之间的整数.
- 1.11 编写程序,提示用户输入两个整数,打印出这两个整数所指定的范围内的所有整数.
- 1.12 下面的for循环完成了什么功能?sum的终值是多少?
- 1.13 使用for循环重做1.4.1节中的所有练习.
- 1.14 对比for循环和while循环,两种形式的优缺点是什么?
- 1.15 编写程序,包含第14页"再探编译"中讨论的常见错误.熟悉编译器生成的错误消息
- 1.16:编写程序,从cin读取一组数,输出其和.
- 1.17:如果输入的所有值都是相等的,本节的程序会输出什么?如果没有重复值,输出又会是怎样的?
- 1.18:编译并运行本节的程序,给它输入全都相等的值.再次运行程序,输入没有重复的值
- 1.19:修改你为1.4.1节练习1.10(第11页)所编写的程序(打印一个范围内的数),使其能处理用户输入的第一个数比第二个数小的情况.
- 1.5 类简介
- 1.6 书店程序
- 第2章 变量和基本类型
- 2.1 基本内置类型
- 2.1:类型int,long,long long和short的区别是什么?无符号类型和带符号类型的区别是什么?float和double的区别是什么?
- 2.2:计算按揭贷款时,对于利率.本金和付款分别应选择何种数据类型?说明你的理由
- 2.3:读程序写结果
- 2.4:编写程序检查你的估计是否正确,如果不正确,请仔细阅读本节直到弄明白问题所在.
- 2.5:指出下述字面值的数据类型并说明每一组内几种字面值的区别
- 2.6:下面两组定义是否有区别,如果有,请叙述之:
- 2.7:下面字面值表示何种含义?它们各自的数据类型是什么?
- 2.8:请先利用转义序列编写一段程序,要求先输出2M,然后转到新一行.修改程序使其先输出2,然后输出制表符,再输出M.最后转到新一行
- 2.2 变量
- 2.3:复合类型
- 2.15:下面的哪个定义是不合法的?为什么?
- 2.16:考查下面的所有赋值然后回答:哪些赋值是不合法的?为什么?哪些赋值是合法的?它们执行了什么样的操作?
- 2.17:执行下面这段代码输出什么结果?
- 2.18:编写代码分别更改指针的值以及指针所指对象的值.
- 2.19:说明指针和引用的主要区别.
- 2.20:请叙述下面这段代码的作用.
- 2.21:请解释下述定义.在这些定义中有非法的么?如果有,为什么?
- 2.22:假设p是一个int型指针,请说明下述代码的含义.
- 2.23:给定指针p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,也请说明原因.
- 2.24:在下面这段代码中为什么p合法而lp非法?
- 2.25:说明下列变量的类型和值.
- 2.4 const限定符
- 2.5 处理类型
- 2.33:利用本节定义的变量,判断下列语句的运行结果.
- 2.34:基于上一个练习中的变量和语句编写一段程序,输出赋值前后变量的内容,你刚才的判断正确么?如果不对,请反复研读本节的示例直到你明白错在何处为止.
- 2.35:判断下列定义推断出的类型是什么,然后编写程序进行验证.
- 2.36:关于下面的代码,请指出每一个变量的类型以及程序结束时它们各自的值.
- 2.37:赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型.也就是说,如果i是int,则表达式i=x的类型就是int&.根据这一特点,请指出下面的代码中每一个变量的类型和值.
- 2.38:说明由decltype指定类型和由auto指定类型有何区别.请举出一个例子,decltype指定的类型和auto指定的类型一样;再举一个例子,decltype指定的类型与auto指定的类型不一样.
- 2.6:自定义数据结构
- 第3章 字符串,向量和数组
- 3.1 命令空间的using声明
- 3.2 标准库类型string
- 3.2:编写一段程序从标准输入中一次读入一整行,然后修改该程序使其一次读入一个词.
- 3.3:请说明string类的输入运算符和getline函数分别是如何处理空白字符的.
- 3.4:编写一段程序读入两个字符串,比较其是否相等并输出结果.如果不相等,输出比较大的那个字符串.改写上述程序,比较输入的两个字符串是否等长,如果不等长,输出长度较大的那个字符串
- 3.5:编写一段程序从标准输入中读入多个字符并将它们连接在一起,输出连接成的大字符串.然后修改上述程序,用空格把输入的多个字符串分隔开来.
- 3.6:编写一段程序,使用范围for语句将字符串内的所有字符用X代替.
- 3.7:就上一题完成的程序而言,如果将循环控制变量的类型设为char将发生什么?先估计一下结果,然后实际编程进行验证.
- 3.8:分别用while循环和传统的for循环重写第一题的程序,你觉得哪种形式更好呢?为什么?
- 3.9:下面的程序有何作用?它合法么?如果不合法?为什么?
- 3.10:编写一段程序,读入一个包含标点符号的字符串,将标点符号去除后输出字符串的剩余部分.
- 3.11:下面的范围for语句合法吗?如果合法,c的类型是什么?
- 3.3 标准库类型vector
- 3.12: 下列vector对象的定义有不正确的吗?如果有,请指出来.对于正确的,描述其具体执行结果;对于不正确的,说明其错误的原因.
- 3.13: 下列的vector对象各包含多少个元素?这些元素的值分别是多少?
- 3.14: 编写一段程序,用cin读入一组整数并把它们存入一个vector对象.
- 3.15: 改写上题的程序,不过这次读入的是字符串
- 3.16: 编写一段程序,把练习3.13中的vector对象的容量和具体内容输出出来.检验你之前的回答是否正确,如果不对,回过头重新学习3.3.1节(第87页)直到弄明白错在何处为止
- 3.17: 从cin读入一组词并把它们存入一个vector对象,然后设法把所有词都改写为大写形式.输出改变后的结果,每个占一行.
- 3.18: 下面的程序合法吗?如果不合法,你准备如何修改?
- 3.19: 如果想定义一个含有10个元素的vector对象,所有元素都是42,请列举出三种不同的实现方法.哪种方法最好呢?为什么?
- 3.20: 读入一组整数并把它们存入一个vector对象,将每对相邻整数的和输出出来.改写你的程序,这次要求先输出第1个和最后1个元素的和,接着输出第2个和倒数第2个元素的和,以此类推.
- 3.4 迭代器介绍
- 3.21: 请使用迭代器重做3.3.3节(第94页)的第一个练习.
- 3.22: 修改之前那个输出text第一段的程序,首先把text的第一段全都改成大写形式,然后再输出它
- 3.23: 编写一段程序,创建一个含有10个整数的vector对象,然后使用迭代器将所有元素的值都变成原来的两倍.输出vector对象的内容,检验程序是否正确
- 3.24: 请使用迭代器重做3.3.3节(第94页)的最后一个练习
- 3.25: 3.3.3节(第93页)划分分数段的程序是使用下标运算符实现的,请利用迭代器改写该程序并实现完全相同的功能
- 3.26: 在100页的二分搜索程序中,为什么用的是mid=beg+(end-beg)/2,而非mid = (beg+end)/2;?
- 3.5 数组
- 3.27:假设txt_size是一个无参数的函数,它的返回值是int.请回答下列哪个定义是非法的?为什么?
- 3.28:下列元素中的值是什么?
- 3.29:相对比vector来说,数组有哪些缺点,请列举一些.
- 3.30:指出下列代码中的索引错误.
- 3.31:编译一段程序,定义一个含有10个int的数组,令每个元素的值就是其下标值.
- 3.32:将上一题刚刚创建的数组拷贝给另外一个数组.利用vector重写程序,实现类似的功能.
- 3.33:对于104页的程序来说,如果不初始化scores将发生什么?
- 3.34:假定p1和p2指向同一个数组中的元素,则下面程序的功能是什么?什么情况下该程序是非法的?
- 3.35:编写一段程序,利用指针将数组中的元素置为0.
- 3.36:编写一段程序,比较两个数组是否相等.再写一段程序,比较两个vector对象是否相等
- 3.37:下面的程序是何含义,程序的输出结果是什么?
- 3.38:在本节中我们提到,将两个指针相加不但是非法的,而且也没什么意义.请解释为什么两个指针相加没什么意义?
- 3.39:编写一段程序,比较两个string对象.再编写一段程序,比较两个C风格字符串的内容
- 3.40 编写一段程序,定义两个字符数组并用字符串字面值初始化它们:接着再定义一个字符数组存放前两个数组拼接后的结果.使用strcpy和strcat把前两个数组的内容拷贝到第三个数组中.
- 3.41:编写一段程序,用整型数组初始化一个vector对象
- 3.42:编写一段程序,将含有整数元素的vector对象拷贝给一个整型数组
- 3.6 多维数组
- 第4章 表达式
- 4.1 基础
- 4.2 算术运算符
- 4.3 逻辑和关系运算符
- 4.4 赋值运算符
- 4.5 递增和递减运算符
- 4.6 成员访问运算符
- 4.7 条件运算符
- 4.21:编写一段程序,使用条件运算符从vector中找到哪些元素的值是奇数,然后将这些奇数值翻倍
- 4.22: 本节的示例程序将成绩划分成high pass、pass和fail三种,扩展该程序使其进一步将60分到75分之间的成绩设定为low pass.要求程序包含两个版本:一个版本只使用条件运算符;另外一个版本使用1个或多个if语句.哪个版本的程序更容易理解呢?为什么?
- 4.23:因为运算符的优先级问题,下面这条表达式无法通过编译.根据4.12节中的表(第147页)指出它的问题在哪里?应该如何修改?
- 4.24:本节的示例程序将成绩划分成high pass、pass和fail三种,它的依据是条件运算符满足右结合律.假如条件运算符满足的是左结合律,求值过程将是怎样的?
- 第5章 语句
- 第6章 函数
- 第7章 类
- 第8章 IO库
- 第9章 顺序容器
- 第10章 泛型算法
- 第11章 关联容器
- 第12章 动态内存
第1章 开始
1.1 编写一个简单的c++程序
1.1 查阅你使用的编译器的文档,确定它所使用的文件命名约定。编译并运行第2页的main程序。
答案:
本人使用的系统为ubuntu,编译器为gcc
int main()
{
return 0;
}
1.2 改写程序,让它返回-1。返回值-1通常当做程序错误的标识。重新编译并运行你的程序,观察你的系统如何处理main返回的错误标识
答案:
int main()
{
return -1;
}
在linux系统中shell返回一个无符号整型,故为255
1.2 初识输入输出
1.3 编写程序,在标准输出上打印Hello,World
答案:
#include<iostream>
int main()
{
std::cout<<"Hello,World"<<std::endl;
return 0;
}
1.4 我们的程序使用加法运算符+来将两个数相加。编写程序使用乘法运算符*,来打印两个数的乘积。
答案:
#include<iostream>
int main()
{
std::cout<<"Enter two numbers:"<<std::endl;
int v1=0,v2=0;
std::cin>>v1>>v2;
std::cout<<"The product of "<<v1<<" and "<<v2<<" = "<<v1*v2<<std::endl;
return 0;
}
1.5 我们将所有输出操作放在一条很长的语句中。重写程序,将每个运算对象的打印操作放在一条独立的语句中。
#include<iostream>
int main()
{
std::cout<<"Enter two numbers:"<<std::endl;
int v1=0,v2=0;
std::cin>>v1>>v2;
std::cout<<"The product of "<<v1<<" and "
<<v2<<" = "<<v1*v2<<std::endl;
return 0;
}
1.6 解释下列程序是否合法
std::cout<<"The sum of "<<v1;
<<" and "<<v2;
<<" is "<<v1+v2<<std::endl;
如果程序是合法的,它输出什么?如果程序不合法,原因何在?应该如何修正?
答案:
不合法 前两行的末尾有分号,表示语句的结束,第2、3两行为两条新的语句。而这两条语句在“<<”之前缺少了输出流,应在“<<”之前加上“std::cout”,即得到正确的程序。
1.3 注释简介
1.7 编译一个包含不正确的嵌套注释的程序,观察编译器返回的报错信息
答案:
/*
*注释对/* */不能嵌套。
*“不能嵌套”几个字会被认为是源码,
*像剩余程序一样处理
*
*/
int main()
{
return 0;
}
运行结果:
1.7.cc:2:18: error: stray ‘\344’ in program
*注释对/* */���能嵌套。
^
1.7.cc:2:19: error: stray ‘\270’ in program
*注释对/* */���能嵌套。
^
1.7.cc:2:20: error: stray ‘\215’ in program
*注释对/* */��能嵌套。
^
1.7.cc:2:21: error: stray ‘\350’ in program
*注释对/* */不���嵌套。
^
1.7.cc:2:22: error: stray ‘\203’ in program
*注释对/* */不���嵌套。
^
1.7.cc:2:23: error: stray ‘\275’ in program
*注释对/* */不��嵌套。
......
1.8 指出下列哪些输出语句是合法的(如果有的话)
std::cout<<"/*";
std::cout<<"*/";
std::cout<</*"*/"*/;
std::cout<</*"*/"/*"/*"*/;
预测编译这些语句会产生什么样的结果,实际编译这些语句来验证你的答案(编写一个小程序,每次将上述一条语句作为其主体),改正每个编译错误。
答案:
预测上面1,2,4句都是正确的。
第三句的报错为:error: missing terminating " character
所以再加上一个双引号就好了
ps:第四条看起来很混乱,但它是正确的.第一个双引号被注释掉了,第四个双引号也被注释掉了,第二个双引号和第三个双引号之间的"/*"被认为是字符串的文字内容,但是,这样的程序风格显然是不好的.
1.4 控制流
1.9 编写程序,使用while循环将50到100的整数相加
#include <iostream>
int main()
{
int val = 50,sum = 0;
while(val <= 100)
{
sum += val;
val++;
}
std::cout<<"Sum of 50 to 100 inclusive is "<<sum<<std::endl;
return 0;
}
1.10 除了++运算符将运算对象的值增加1之外,还有一个递减运算符(–)实现将值减少1.编写程序,使用递减运算符在循环中按递减顺序打印出10到0之间的整数.
#include <iostream>
int main()
{
int val = 10;
while(val >= 0)
{
std::cout<<val<<std::endl;
val--;
}
return 0;
}
1.11 编写程序,提示用户输入两个整数,打印出这两个整数所指定的范围内的所有整数.
#include <iostream>
int main()
{
int v1 = 0,v2 = 0;
std::cout<<"please input two numbers:";
std::cin>>v1>>v2;
if(v1>v2)
{
while(v1>=v2)
{
std::cout<<v1--<<" ";
}
}
else
{
while(v1<=v2)
{
std::cout<<v2--<<" ";
}
}
std::cout<<std::endl;
return 0;
}
1.12 下面的for循环完成了什么功能?sum的终值是多少?
int sum = 0;
for(int i = -100; i <= 100 ; i++)
sum+=i;
答案:
该for循环完成了从-100到100范围内所有整数的相加(包括-100和100),sum的终值是0
1.13 使用for循环重做1.4.1节中的所有练习.
答案:
1.9
#include <iostream>
int main()
{
int sum = 0;
for(int val=50 ; val <= 100 ; val++)
{
sum+=val;
}
std::cout<<"Sum of 50 to 100 inclusive is "<<sum<<std::endl;
return 0;
}
1.10
#include <iostream>
int main()
{
for(int val=10;val>=0;val--)
{
std::cout<<val<<" ";
}
std::cout<<std::endl;
return 0;
}
1.11
#include <iostream>
int main()
{
int v1 = 0,v2 = 0;
std::cout<<"please input two numbers:";
std::cin>>v1>>v2;
if(v1>v2)
{
for(v1 ; v1 >= v2 ; v1--)
{
std::cout<<v1<<" ";
}
}
else
{
for(v2;v2 >= v1 ; v2--)
{
std::cout<<v2<<" ";
}
}
std::cout<<std::endl;
return 0;
}
转载请标原著地址:点击链接进入原著
https://blog.csdn.net/qq130106486/article/details/103810949
1.14 对比for循环和while循环,两种形式的优缺点是什么?
答案:
在循环次数已知的情况下,for循环的形式显然更为简洁.
在循环次数无法预知时,用while循环实现更适合.用特定条件控制循环是否执行,循环体中执行的语句可能导致循环判定条件发生变化
1.15 编写程序,包含第14页"再探编译"中讨论的常见错误.熟悉编译器生成的错误消息
答:
语法错误(syntax error):
#include <iostream>
//错误:main的参数列表漏掉了
int main ({
//错误:endl后使用了冒号而非分号
std::cout<<"Read each file."<<std::endl:
//错误:字符串字面常量的两侧漏掉了引号
std::cout<<Update master.<<std::endl;
//错误:漏掉了第二个输出运算符
std::cout<<"Write new master."std::endl;
//错误:return语句漏掉了分号
return 0
}
编译器的错误提示:
类型错误(type error):
#include <iostream>
void temp(int v)
{
std::cout<<"the v is "<<v<<std::endl;
}
int main()
{
temp("hello");
return 0;
}
编译器的错误提示:
声明错误(declaration error):
#include <iostream>
int main()
{
int v1=0,v2=0;
std::cin>>v>>v2;//错误:使用了"v"而非"v1"
//错误:cout未定义;应该是std::cout
cout<<v1+v2<<std::endl;
return 0;
}
编译器的错误提示:
1.16:编写程序,从cin读取一组数,输出其和.
答案:(ps:linux和mac中结束符为Ctrl+D;windows中结束符为Ctrl+D)
#include<iostream>
int main()
{
int value=0,sum=0;
while(std::cin>>value)
{
sum += value;
}
std::cout<<"Sum is "<<sum<<std::endl;
return 0;
}
1.17:如果输入的所有值都是相等的,本节的程序会输出什么?如果没有重复值,输出又会是怎样的?
答案:
如果输入值都是相等的,则while循环中的else分支永远不会执行,直到输入结束,while循环退出,循环后的输出语句打印这唯一的一个值和它出现的次数
如果没有重复值,则while循环中的if语句的真值分支永远不会执行,每读入一个值,都会进入else分支,打印它的值和出现次数1.输入结束后,while循环退出,循环后的输出语句打印最后一个值和出现次数1
1.18:编译并运行本节的程序,给它输入全都相等的值.再次运行程序,输入没有重复的值
答案:
#include <iostream>
int main()
{
int cnt =0 , currVal = 0 , val = 0;
if(std::cin >> currVal)
{
cnt = 1;
while(std::cin>> val)
{
if(currVal == val)
{
cnt++;
}
else
{
std::cout<<currVal<<" occurs "
<<cnt<<" times"<<std::endl;
currVal = val;
cnt = 1;
}
}
std::cout<<currVal<<" occurs "
<<cnt<<" times"<<std::endl;
}
return 0;
}
运行程序截图:
1.19:修改你为1.4.1节练习1.10(第11页)所编写的程序(打印一个范围内的数),使其能处理用户输入的第一个数比第二个数小的情况.
答:
#include<iostream>
int main()
{
std::cout<<"请输入俩个数(第一个数比第二个数小):" << std::endl;
int v1=0,v2=0;
std::cin>>v1>>v2;
if(v1 < v2)
{
while(v1<=v2)
{
std::cout<<v1++<<" ";
}
}
else
{
std::cout<<"输入有误."<<std::endl;
}
std::cout<< std::endl;
return 0;
}
1.5 类简介
1.20:在网站http://www.informit.com/title/0321714113上第1章的代码目录包含了头文件Sales_item.h 将它拷贝到你自己的工作目录中.用它编写一个程序,读取一组书籍销售记录,将每条记录打印到标准输出上.
答案:
首先在此贴出头文件Sales_item.h的下载链接(ps:网站上总共给出了4个版本,见下):
1.GCC 4.7.0 版本(ps:适用于GCC 4.7.0或者更高版本的GCC,作者所用系统为ubuntu编译器为GCC 8.3.0 因此采用的此版本) 点击链接开始下载
2.GCC pre-C++ 11 compilers 2012 版本(ps:适用于GCC 4.7.0之前的版本,不适用c++0x或c++11的新标准) 点击链接开始下载
3.MS Visual Studio 2012(ps:适用于Visual Studio 2012或更高版本) 点击链接开始下载
4.Microsoft pre-C++ 11 compilers版本(ps:适用于Visual Studio 2012之前的版本,不适用与c++0x或c++11的新标准) 点击链接开始下载
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item salesItem;
while(std::cin>>salesItem)
{
std::cout<<salesItem<<std::endl;
}
return 0;
}
运行截图:
转载请标原作地址:点击链接进入原作
原作地址 https://blog.csdn.net/qq130106486/article/details/103810949
1.21:编写程序,读取两个ISBN相同的Sales_item对象,输出它们的和.
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item book1, book2;
std::cout << "请输入两个ISBN相同的Sales_item.h对象:"<< std::endl;
std::cin >> book1 >> book2;
if (compareIsbn(book1, book2))
{
std::cout<<std::endl<<"汇总信息:ISBN 售出本书 销售额 和 平均售价为:"<<std::endl
<<book1+book2<<std::endl;
}
else
{
std::cout<<"您的输入有误"<<std::endl;
}
return 0;
}
运行截图:
1.22:编写程序,读取多个具有相同ISBN的销售记录,输出所有记录的和.
答:代码如下,运行截图随后!
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item trans,total;
std::cout<<"请输入相同ISBN的销售记录"<<std::endl;
if(std::cin>>total)
{
while(std::cin>>trans)
{
if(compareIsbn(trans,total))
{
total = total + trans;
}
else
{
std::cout<<"ISBN不同"<<std::endl;
return -1;
}
}
}
else
{
std::cout<<"没有数据"<<std::endl;
return -1;
}
std::cout<<std::endl<<"所有输入记录的和为:ISBN 售出本书 销售额 和 平均售价为:"<<std::endl
<<total<<std::endl;
return 0;
}
运行截图:
1.23:编写程序,读取多条销售记录,并统计每个ISBN(每本书)有几条销售记录.
答案:代码如下,运行截图随后
//ps:在这里我们所指的是所有相同ISBN号的销售输入都相连输入
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item tran1,tran2;
int num=0;
std::cout<<"请输入销售记录:"<<std::endl;
if(std::cin>>tran1)
{
num++;
while(std::cin>>tran2)
{
if(compareIsbn(tran1,tran2))
{
num++;
}
else
{
std::cout<<std::endl<<"ISBN:"<<tran1.isbn()<<
"的销售记录有"<<num<<"条"<<std::endl;
tran1 = tran2;
num = 1;
}
}
std::cout<<std::endl<<"ISBN:"<<tran1.isbn()<<
"的销售记录有"<<num<<"条"<<std::endl;
}
else
{
std::cout<<"没有输入"<<std::endl;
return -1;
}
return 0;
}
运行截图如下:
1.24:输入表示多个ISBN的多条销售记录来测试上一个程序,每个ISBN的记录应该聚在一起
答案:
测试记录如下:
0-201-70353-X 4 24.99
0-201-70353-B 5 24.99
0-201-70353-B 6 24.99
0-201-70353-C 7 24.99
0-201-70353-D 8 24.99
0-201-70353-D 9 24.99
0-201-70353-D 10 24.99
0-201-70353-D 11 24.99
运行测试记录截图如下:
1.6 书店程序
1.25:借助网站上的Sales_item.h头文件,编译并运行本节给出的书店程序.
答案:代码及其运行代码读取的文件(ps:使用重定向将文件作为程序运行的输入),随后贴出了运行截图
代码:
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item total;
if(std::cin>>total)
{
Sales_item trans;
while(std::cin>>trans)
{
if(trans.isbn() == total.isbn())
{
total += trans;
}
else
{
std::cout<<total<<std::endl;
total = trans;
}
}
std::cout<<total<<std::endl;
}
else
{
std::cerr<<("No data?!")<<std::endl;
return -1;
}
return 0;
}
记录文件:input.txt
0-201-70353-X 4 24.99
0-201-70353-B 5 24.99
0-201-70353-B 6 24.99
0-201-70353-C 7 24.99
0-201-70353-D 8 24.99
0-201-70353-D 9 24.99
0-201-70353-D 10 24.99
0-201-70353-D 11 24.99
运行截图:
原创不易,第一章结束markdown已经堆了接近一万字了,转载请标原作地址:点击链接进入原作
原作地址 https://blog.csdn.net/qq130106486/article/details/103810949
第2章 变量和基本类型
2.1 基本内置类型
2.1:类型int,long,long long和short的区别是什么?无符号类型和带符号类型的区别是什么?float和double的区别是什么?
答案:
首先int,long,long long和short都属于整型,区别是C++标准规定的最小值(即该类型在内存中所占的比特数)不同.其中,short是短整型,占16位;int是整型,占16位,long和long long均为长整型,分别占32位和64位.C++标准允许不同的编译器赋予这些类型更大的尺寸.某一类型占的比特数不同,他所能表示的数据范围也不一样.
大多数整型都可以划分为无符号类型和带符号类型,在无符号类型中所有比特都用来存储数值,但是仅能表示大于等于0的数;带符号类型则可以表示正数,负数或0.
float和double分别是单精度浮点数和双精度浮点数,区别主要是在内存中所占的比特数不同,以及默认规定的有效位数不同.
2.2:计算按揭贷款时,对于利率.本金和付款分别应选择何种数据类型?说明你的理由
答案:
在实际应用中,利率,本金和付款既有可能是整数,也有可能是普通的实数.因此应该选择一种浮点类型来表示.在三种可供选择的 浮点类型float,double和long double中,double和float的计算代价比较接近且表示范围更广,long double的计算代价则相对比较大,一般情况下没有选择的必要.综合以上分析,选择double是比较恰当的.
点击链接进入原作
原作地址 https://blog.csdn.net/qq130106486/article/details/10381094
2.3:读程序写结果
答案:
见每行后的注释
std::cout<<u2 - u <<std::endl; //输出32
std::cout<<u - u2 <<std::endl; //输出4294967264
std::cout<<i2 - i <<std::endl;//输出32
std::cout<<i - i2 <<std::endl;//输出-32
std::cout<<i -u <<std::endl; //输出0
std::cout<<u -i <<std::endl; //输出0
2.4:编写程序检查你的估计是否正确,如果不正确,请仔细阅读本节直到弄明白问题所在.
答案:
编写程序代码如下,运行截图随后(其中涉及到了计算机组成原理的一些知识,比如负数转化无符号数时要转化为补码)
#include<iostream>
int main()
{
unsigned u = 10,u2 = 42;
std::cout<<u2 - u <<std::endl; //输出32
std::cout<<u - u2 <<std::endl;
/*
*我的机器int类型占4个字节也就是32位,-42的理论结果是-32
*u-u2也就是10加上-42
*-42的二进制表示为: 11111111 11111111 11111111 11010110
* 10的二进制表示为: 00000000 00000000 00000000 00001010
* 运算结果为: 11111111 11111111 11111111 11100000
* 换算成无符号数为: 4294967264
*/
int i =10,i2 = 42;
std::cout<<i2 - i <<std::endl;//输出32
std::cout<<i - i2 <<std::endl;//输出-32
std::cout<<i -u <<std::endl;
std::cout<<u -i <<std::endl;
/*
* i-u 首先i会被转化为无符号数
* i 的二进制表示为: 00000000 00000000 00000000 00001010
* -u 的二进制表示为: 11111111 11111111 11111111 11110110
* i-u为:1 00000000 00000000 00000000 00000000
* 化成十进制为: 应为最高的1溢出了,所以结果为0
* u-i 首先-i会被转化为无符号数
* -i 的二进制表示为: 11111111 11111111 11111111 11110110
* u 的二进制表示为: 00000000 00000000 00000000 00001010
* u-i为:1 10000000 00000000 00000000 00000000
* 化成十进制为: 应为最高的1溢出了,所以结果为0
*/
return 0;
}
运行截图:
原作地址 https://blog.csdn.net/qq130106486/article/details/10381094
2.5:指出下述字面值的数据类型并说明每一组内几种字面值的区别
(a) ‘a’,L’a’,“a”,L"a"
(b) 10,10u,10L,10uL,012,0xC
(c ) 3.14 , 3.14f , 3.14L
(d) 10 , 10u , 10. , 10e-2
答案:
(a)组: 'a’表示字符 a , L’a’表示宽字符型字面值 a 且类型是wchar_t , "a"表示字符串 a , L"a"表示宽字符字符串 a ,且类型是wchar_t
(b)组:10表示整型字面值 , 10u表示一个无符号整数 , 10L表示一个长整形数,字面值的类型最少是long , 10uL表示的是无符号长整型字面值 , 012是一个八进制数 , 对应的十进制数为10 , 0xC是一个十六进制数 , 对应的十进制数为12.
(c )组:3.14的数据类型是浮点型字面常量,3.14f的表示一个float型的单精度浮点数 , 3.14L表示一个long double类型的扩展精度浮点数
(d)组:10是一个整数 , 10u是一个无符号整数 , 10. 是浮点型字面值 , 10e-2是一个用科学计数法标的浮点数,大小为0.1.
2.6:下面两组定义是否有区别,如果有,请叙述之:
答案:见代码区的注释
int month = 9 , day = 7;//十进制数9和7
int month =06 , day = 07;//09代表八进制数,不过9已经超出了八进制的范围,所以这个数是错的,07代表八进制数7化为十进制也是7
2.7:下面字面值表示何种含义?它们各自的数据类型是什么?
答案:见代码区注释,运行截图随后
#include <iostream>
int main()
{
std::cout<<"Who goes with F\145rgus?\012";// \145八进制数表示e,\012八进制数表示\n
std::cout<<3.14e1L<<std::endl;//采用科学计数法表示的扩展精度浮点数,其值是31.4
std::cout<<1024.f<<std::endl; //1024是一个float的单精度浮点数,但是该形式在某些编译器中会报错,比如说笔者的,因为后缀f直接跟在整数1024之后;改写成1024.f就可以了
std::cout<<3.14L<<std::endl;//3.14是一个扩展精度浮点数
return 0;
}
运行截图:
2.8:请先利用转义序列编写一段程序,要求先输出2M,然后转到新一行.修改程序使其先输出2,然后输出制表符,再输出M.最后转到新一行
答案:(PS:我在下面使用了广义的转义字符,也就是全部将其用八进制表示,其中\115可以用\0x4d或者M代替,\011可以用\t代替,\012可以用\n代替)
#include <iostream>
int main()
{
std::cout<<"\062\115\012";
std::cout<<"\062\011\115\012";
return 0;
}
运行截图:
2.2 变量
2.9:解释下列定义的含义.对于非法的定义,请说明错在何处并将其改正.
答案:见代码区的注释
#include <iostream>
int main()
{
/*
*std::cin>>int input_value;
*输入运算符的右侧需要一个明确的变量名称,而不是一个变量定义语句.所以这是一种错误的写法.
*故应先定义变量,然后进行使用
*正确写法如下:
*/
int input_value;
std::cin>>input_value;
/*
* int i = {3.14};
* 使用花括号进行列表初始化,如果初始值存在丢失的风险,编译器会报错.
* 3.14赋给整型变量,会丢失数据精度,这是一种不被建议的窄化操作,故编译器会报错
* 正确的写法如下:
*/
int i = {3};
/*
*double salary = wage = 9999.99;
* 在声明语句中,声明多个变量时,需要用逗号分开,而不能直接用等号运算符连接
*正确写法如下:
*/
double wage,salary;
salary = wage = 9999.99;
//进行初始化,将浮点型字面值赋给整型,会进行转化,丢掉小数点的部分,这也是一种不被建议的窄化操作
int j =3.14;
std::cout<<j<<std::endl;
return 0;
}
运行截图:
2.10:下列变量的初值分别是什么?
答案:见代码区的注释
#include <iostream>
/*
*string不是内置类型,
*而且string类型本身就接受无参数的初始化方法,
*所以无论在函数内还是函数外都默认初始化空串
*/
std::string global_str;
int global_int; //被默认初始化为0
int main()
{
/*
*int是内置类型,它将不被初始化,
*如果程序试图输出或者拷贝未初始化的变量,
*将得到一个未定义的奇异值
*/
int local_int;
std::string local_str;
/*
*string不是内置类型,
*而且string类型本身就接受无参数的初始化方法,
*所以无论在函数内还是函数外都默认初始化空串
*/
std::cout << global_int << std::endl;
std::cout << global_str << std::endl;
std::cout << local_int << std::endl;
std::cout << local_str << std::endl;
return 0;
}
运行截图:
2.11:指出下面的语句是声明还是定义:
答案:见代码区注释
#include <iostream>
int main()
{
/*
*错误的定义,
*因为在函数体内部
*试图初始化一个由extern关键字标记的变量,
*将引发错误
*/
extern int ix = 1024;
//定义
int iy;
//声明
extern int iz;
return 0;
}
运行截图:
2.12:请指出下面的名字中哪些是非法的?
答案:见代码区的注释
#include <iostream>
int main()
{
//int double = 3.14; 非法,使用了c++关键字
int _;//正确
/*int catch-22; 非法,
*c++标识符只能由字母,下划线和数字组成,
*不能出现-,
*如果换成_即catch-22就是正确的
*/
//int 1_or_2;非法,标识符只能以下划线或字母开头
double Double = 3.14;//正确
return 0;
}
转载请标原作地址:点击链接进入原作
原作地址 https://blog.csdn.net/qq130106486/article/details/103810949
2.13:下面程序中j的值是多少?
答案:见代码块的注释
#include <iostream>
int i = 42;
int main()
{
int i = 100;
int j =i;//这里的j的值是100
std::cout<<"j:"<<j<<std::endl;
return 0;
}
运行截图:
2.14:下面的程序合法么?如果合法,它将输出什么?
答案:见代码块注释
#include <iostream>
int main()
{
int i = 100,sum = 0;
for(int i = 0;i != 10 ; i++)
sum += i;
std::cout<<i<<" "<<sum<<std::endl;
}
/*
*合法
* i = 100 ;sum 为1到9的和为45
*/
运行截图:
2.3:复合类型
2.15:下面的哪个定义是不合法的?为什么?
#include <iostream>
int main()
{
int ival = 1.01;//合法,但这是一种不建议的窄化类型的方式
//int &rval1 = 1.01; 不合法,引用只能绑定对象,不能绑定字面值常量
int &rval2 = ival;
//int &rval3; 不合法,因为我们无法将引用重新绑定到另外一个对象上,引用必须初始化
return 0;
}
2.16:考查下面的所有赋值然后回答:哪些赋值是不合法的?为什么?哪些赋值是合法的?它们执行了什么样的操作?
答案:见代码块中的注释
#include <iostream>
int main()
{
int i = 0,&r1 =i; double d = 0,&r2 = d;
//(a):
r2 = 3.14159;//将d赋值为3.14159
std::cout<<"d="<<d<<" r2="<<r2<<std::endl;
//(b):
r2=r1;//将i的值赋给d,d的值为0
std::cout<<"d="<<d<<" r2="<<r2<<std::endl;
//(c):
i=r2;//将d的值赋给i,应为d的值此时为0,故i的值为0
std::cout<<"i="<<i<<" r1="<<r1<<std::endl;
//(d):
r1=d;//将d的值赋给i,因为此时d的值为0,故i的值为0;
std::cout<<"i="<<i<<" r1="<<r1<<std::endl;
return 0;
}
运行截图:
原作不易:点击链接进入原作
原作地址 https://blog.csdn.net/qq130106486/article/details/103810949
2.17:执行下面这段代码输出什么结果?
答案:见代码块中的注释
#include <iostream>
int main()
{
int i,&ri = i;
i = 5;ri = 10;
std::cout<<i<<" "<<ri<<std::endl;//输出10 10
return 0;
}
运行截图:
2.18:编写代码分别更改指针的值以及指针所指对象的值.
答案:见代码
#include <iostream>
int main()
{
int i = 6 , j = 8;
//更改指针的值
int *p = &i;
std::cout<<p<<" "<<*p<<std::endl;
//更改指针的值
p = &j;
std::cout<<p<<" "<<*p<<std::endl;
//更改指针所指对象的值
*p = 88;
std::cout<<p<<" "<<*p<<std::endl;
//更改指针所指对象的值
j = 30;
std::cout<<p<<" "<<*p<<std::endl;
//更改指针的值
p = &i;
std::cout<<p<<" "<<*p<<std::endl;
return 0;
}
运行截图:
2.19:说明指针和引用的主要区别.
答案:
相同点:指针"指向"内存中的某个对象,而引用"绑定到"内存中的某个对象,他们都实现了对其他对象的间接访问.
不同点:
1.指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以指向几个不同的对象;引用不是一个对象,无法令引用重新绑定到另外一个对象.
2.引用无需在定义时赋初值,和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值;引用必须在定义时赋初值.,就无法在绑定到其他的对象,每次访问这个引用都是最初绑定的那个对象.
2.20:请叙述下面这段代码的作用.
答案:见代码块儿的注释
#include <iostream>
int main()
{
int i =42;
int *p1 = &i;
*p1 = *p1 * *p1;
/*
*定义了一个变量i,定义一个指针p1指向i,
* p1所指对象的值与p1所指对象的值相乘,
* 并赋值给p1所指的对象,故*p1=42*42=1764
*/
std::cout<<"*p1 = "<<*p1<<std::endl;
return 0;
}
运行截图:
2.21:请解释下述定义.在这些定义中有非法的么?如果有,为什么?
答案:见代码块儿注释
#include <iostream>
int main()
{
int i = 0;
//(a)
//double * dp = &i; 非法的,dp指针是指向double类型的指针,不能让其指向Int类型
//(b)
//int *ip = i; 非法的,指针存放某个对象的地址,不能用变量赋值,正确的做法是通过取地址运算符&i得到变量i在内存中的地址,然后再将该地址赋值给指针
//(c)
int *p = &i;//合法
}
2.22:假设p是一个int型指针,请说明下述代码的含义.
答案:见代码块儿注释
#include <iostream>
int main()
{
int *p;
p = nullptr;
if(p)//检验的是指针本身的值,即指针所指的地址值,如果指针指向一个真实存在的变量,则其值必不为0,则其值必不为0,此时为真;如果指针没有指向对象或者是无效指针,则对p的使用将引发不可预计的后果.
{
std::cout<<"p指针不为nullptr"<<std::endl;;
}
else
{
std::cout<<"p指针为nullptr"<<std::endl;
}
int i = 0;
p = &i;
if (*p)//解引用运算符*p作为if的语句条件时,实际检验的是指针所指的对象内容,如果内容为0,则条件为假,否则,条件为真
{
std::cout<<"p指针所指的对象的值不为0"<<std::endl;
}
else
{
std::cout<<"p指针所指的对象的值为0"<<std::endl;
}
return 0;
}
运行截图:
2.23:给定指针p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,也请说明原因.
答案:
在C++程序中,应该尽量初始化所有指针,并且尽可能等定义了对象之后再定义指向它的指针.如果实在不清楚应该指向何处,就把它初始化为nullptr或者0,这样程序就能检测并知道它有没有指向一个具体的对象了.其中nullptr是C++11新标准刚刚引入的一个特殊字面值,它可以转换成任意其他的指针类型.
如果不注意初始化所有指针而贸然判断指针的值,则有可能引发不可预知的结果.一种处理的办法是把if§置于try结构中,当程序块顺利执行时,表示p指向了合法的对象;当程序块出错跳转到catch语句时,表示p没有指向合法的对象.
2.24:在下面这段代码中为什么p合法而lp非法?
#include <iostream>
int main()
{
int i = 42;
void *p = &i;//void *指针是一种特殊类型的指针,可用于存放任意对象的地址,故p指针是合法的
//long *lp = &i; 错误,试图把int型对象的地址赋给long型指针
return 0;
}
2.25:说明下列变量的类型和值.
答案:见代码块中的注释
#include<iostream>
int main()
{
//(a)
int *ip,i,&r=i;
/*
*ip是一个整型指针,指向了一个整型数,它的值是所指整型数在内存中的地址;
*i是整型数;
*r是一个引用,它绑定了i,可以看作是i的别名,r的值就是i的值
*/
//(b)
int i,*ip = 0;
/*
*i是一个整型数;
*ip是一个整型指针,但是它不指向任何具体的对象,它的值被初始化为0
*/
//(c)
int *ip,ip2;
/*
*ip是一个整数指针,指向一个整型数,它的值是所指整型数在内存中的地址;
*ip2是一个整型数
*/
return 0;
}
写作不易:点击链接进入原作
转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
2.4 const限定符
2.26:下面哪些句子是合法的?如果有不合法的句子,请说明为什么?
答案:见代码块注释
#include <iostream>
int mian()
{
//(a)
//const int buf;const对象一旦创建后其值就不能再改变,所以const对象必须初始化
//(b)
int cnt = 0;//合法
//(c)
const int sz = cnt;//合法
//(d)
++cnt;//合法
//++sz;不合法,const对象其值不能被改变,所以不能执行自加操作
return 0;
}
2.27:下面的哪些初始化是合法的?请说明原因.
答案:见代码块儿注释:
#include <iostream>
int main()
{
int i = -1,&r =0;//不合法 ,非常量引用r不能引用字面值常量0
int *const p2 = &i2;//合法,p2是一个常量指针,p2的值永不改变,即p2永远指向变量i2
const int i = -1,&r = 0;//合法,i是一个常量,r是一个常量引用,此时r可以绑定到字面值常量0
const int *const p3 =&i2;//合法,p3是一个常量指针,p3的值永不改变,即p3永远指向变量i2;
//同时p3指向的是常量,即我们不能通过p3改变所指对象的值
const int *p1 =&i2;//合法,p1指向一个常量,我们不能通过p1改变所指对象的值
const int &const r2;//不合法,引用本身不是对象,不能让引用恒定不变
const int i2 = i,&r = i;//合法,i2是一个常量,r是一个常量引用
return 0;
}
2.28:说明下面的这些定义是什么意思,挑出其中不合法的.
答案:见代码块中的注释
#include <iostream>
int main()
{
int i,*const cp;
/*
*定义了一个整型数i,
*非法的,cp是一个常量指针,因其值不能改变,所以必须初始化
*/
int *p1,*const p2;
/*
*定义了一个指向整型数的指针
*非法的,cp2是一个常量指针,因其值不能改变,所以必须初始化
*/
const int ic , &r =ic;
/*
*非法的,ic是一个常量,因其值不能改变,所以必须初始化
*定义了一个常量引用绑定了整型常量ic
*/
const int *const p3;
/*
*非法的,p3是一个常量指针,因其值不能改变,所以必须初始化;
*同时p3指向的是常量,即我们不能通过p3改变所指对象的值
*/
const int *p;
/*
*定义了一个指向整型常量的指针,且没有指向任何实际的对象
*/
return 0;
}
2.29:假设已有上一个练习中定义的那些变量,则下面的那些语句是合法的?请说明原因.
答案:见代码块儿注释
#include <iostream>
int main()
{
//(a): i = ic
i = ic;//合法的,常量ic的值赋给了非常量i
//(b):p1 = p3
p1=p3;//非法的,普通指针p1指向了一个常量,从语法上说,p1的值可以随意改变,显然是不合理的
//(c):p1 = &ic
p1=⁣//非法的,也就是不能将一个常量赋值给一个普通指针
//(d):p3=&ic
p3=⁣//非法的,常量指针p3不能被赋值
//(e):p2=p1
p2 = p1;//非法的,常量指针p2不能被赋值
//(f):ic = *p3
ic = *p3;//非法的,常量ic不能被赋值
return 0;
}
2.30:对于下面的这些语句,请说明对象被声明成了顶层const还是底层const?
答案见代码块儿注释
#include <iostream>
int main()
{
const int v2 =0;//顶层const,表示一个整型常量
int v1 = v2;//没有被声明成const
int *p1 = &v1,&r1 = v1;//没有被声明成const
const int *p2 = &v2,*const p3 = &v1 ,&r2 = v2;
/*
*p2是底层const,表示它所指的对象是个常量
*p3既是顶层const,又是底层const,即表示一个整型常量指针,又表示它所指的对象是个常量
*r2是底层const,表示它引用的对象是个常量
* */
return 0;
}
2.31:假设已有上一个练习中所做的那些声明,则下面的哪些语句是合法的?请说明顶层const和底层const在每个例子中有何体现.
答案:见代码块儿注释
#include<iostream>
int main()
{
r1 = v2;//合法的,r1是一个非常量引用,v2是顶层const,把v2的值拷贝给r1不会对v2有任何影响
//p1 = p2;不合法,因为p1是普通指针,指向的对象可以是任意值,p2是指向常量的底层const,令p1指向p2可能会错误修改常量的值
p2 = p1;//合法,因为p1是普通指针,p2是指向常量的底层const,只不过是我们不会通过p2去修改所指对象的值
//p1 = p3;//不合法,因为p1是普通指针,p3包含底层const,不能把p3的值赋值给普通指针
p2 = p3;//合法,p2和p3包含相同的底层const,p3的顶层const可以忽略不计
return 0;
}
2.32:下面的代码是否合法?如果非法,请设法将其修改正确.
答案:见代码块儿注释
#include <iostream>
int main()
{
//int null = 0,*p = null;null并不是一个关键字,因此只是一个普通的变量名,
//*p是一个指针,给指针赋值应该赋值地址,所以在null前应该加&
int null = 0,*p = &null;//或者 *p=nullptr;
return 0;
}
2.5 处理类型
2.33:利用本节定义的变量,判断下列语句的运行结果.
答案:见代码块注释
#include<iostream>
int main()
{
auto i =0,&r = i;
auto a = r;
const int ci = i,&cr = ci;
auto b = ci;
auto c = cr;
auto d = &i;
auto e = &ci;
auto &g = ci;
a = 42;
/*
*正确
*i是一个整数,r是一个整型引用
*所以a是一个整型数
*/
b = 42;
/*
*正确
*ci是一个整型常量,
*auto会忽略掉ci的顶层const
*所以b为整型数
*/
c = 42;
/*
* 正确
* cr是ci的别名,也是整型常量
* auto会忽略掉ci的顶层cons
* 所以c为整型数
*/
//d=42;
/*
*错误
*i是一个整型数,&i是一个整型地址
*所以d是一个整型指针
*指针不能用字面值常量为其赋值
*/
//e=42;
/*
*错误
*ci是一个整型常量,&ci是一个整型常量地址
*所以e是指向整型常量的指针
*指针不能用字面值常量为其赋值
*/
//g=42;
/*
*错误
*ci是一个整型常量
*auto类型的引用对初始值的顶层const属性保留
*所以g是绑定到整型常量的引用
*故g不能执行赋值操作
*/
return 0;
}
2.34:基于上一个练习中的变量和语句编写一段程序,输出赋值前后变量的内容,你刚才的判断正确么?如果不对,请反复研读本节的示例直到你明白错在何处为止.
答案:见下面代码块
#include<iostream>
int main()
{
auto i =0,&r = i;auto a = r;
const int ci = i,&cr = ci;
auto b = ci;
auto c = cr;
auto d = &i;
auto e = &ci;
auto &g = ci;
std::cout<<"begin a = "<<a<<std::endl;
a = 42;
std::cout<<"after a = "<<a<<std::endl;
std::cout<<"begin b = "<<b<<std::endl;
b = 42;
std::cout<<"after b = "<<b<<std::endl;
std::cout<<"begin c = "<<c<<std::endl;
c = 42;
std::cout<<"after c = "<<c<<std::endl;
std::cout<<"begin d = "<<d<<std::endl;
//d=42; d是指针,赋值错误
std::cout<<"begin e = "<<e<<std::endl;
//e=42; e是指针,赋值错误
std::cout<<"begin g = "<<g<<std::endl;
//g=42; g是常量引用,赋值错误
return 0;
}
运行截图:
2.35:判断下列定义推断出的类型是什么,然后编写程序进行验证.
答案:见下面代码
#include <iostream>
#include<typeinfo>
int main()
{
const int i = 42;//整型常量
std::cout<<typeid(i).name()<<std::endl;
auto j = i;//整型数
std::cout<<typeid(j).name()<<std::endl;
auto &k = i;//整型常量
std::cout<<typeid(k).name()<<std::endl;
auto *p =&i;//指向整型常量的指针
std::cout<<typeid(p).name()<<std::endl;
const auto j2 = i,&k2 = i;//j2是整型数,k2是整型数的引用
std::cout<<typeid(j2).name()<<std::endl;
std::cout<<typeid(k2).name()<<std::endl;
return 0;
}
运行截图:
写作不易,markdown已经码了两万三千字了:点击链接进入原作
转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
2.36:关于下面的代码,请指出每一个变量的类型以及程序结束时它们各自的值.
答案:见代码块儿注释
#include <iostream>
#include <typeinfo>
int main()
{
int a = 3,b =4;
decltype (a) c =a; //c的类型是int
decltype ((b)) d = a;//d的类型是int&
++c;
++d;
/*
*程序输出时:c的值为4,
* d的值为4;
*/
std::cout<<"c的类型为 : "<<typeid(c).name()<<" c = "<<c<<std::endl;
std::cout<<"d的类型为 : "<<typeid(d).name()<<" d = "<<d<<std::endl;
/*
*这里需要注意的是typeid().name()在gcc环境下打印i是代表int
*而且const和引用属性会被丢掉
*/
return 0;
}
运行截图:
2.37:赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型.也就是说,如果i是int,则表达式i=x的类型就是int&.根据这一特点,请指出下面的代码中每一个变量的类型和值.
答案:见代码块儿注释
#include <iostream>
#include<typeinfo>
int main()
{
int a = 3 ,b =4;
decltype (a) c = a;//c的类型是int
decltype(a = b) d = a;//d的类型是int&
//c的值为3
//decltype 并不会计算a =b 只会推测其类型 d的值为3
std::cout<<"c的类型为 : "<<typeid(c).name()<<" c = "<<c<<std::endl;
std::cout<<"d的类型为 : "<<typeid(d).name()<<" d = "<<d<<std::endl;
/*
*这里需要注意的是typeid().name()在gcc环境下打印i是代表int
*而且const和引用属性会被丢掉
*/
return 0;
}
运行截图:
2.38:说明由decltype指定类型和由auto指定类型有何区别.请举出一个例子,decltype指定的类型和auto指定的类型一样;再举一个例子,decltype指定的类型与auto指定的类型不一样.
答案:
decltype和auto的区别:
1.auto类型说明符用编译器计算变量的初始值来推断其类型,而decltype虽然也让编译器分析表达式并得到它的类型,他是不实际计算表达式的值.
2.编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则.例如,auto一般会忽略顶层const,而把底层const保留下来.与之相反,decltype会保留变量的顶层const
3.与auto不同,decltype的结果类型与表达式形式密切相关,如果变量名加上了一对括号,则得到的类型与不加括号时会有不同.如果decltype使用的是一个不加括号的变量,则得到的结果是该变量的类型;如果给变量加上了一层或多层括号,则编译器推断得到引用类型.
例子见下面的代码块:
#include <iostream>
#include <typeinfo>
int main()
{
int i = 666;
auto k1 = i;//k1的类型为int
decltype (i) k2 = i;//k2的类型为int
decltype((i)) k3 =i;//k3的类型是int&
const int j = 888;
auto k4 = j;//k4的类型为int
decltype (j) k5 = j;//k5的类型为const int
decltype((j)) k6 = j;//k6的类型是const int&
k1++,k2++,k3++,k4++;
//k5++;整型常量,不能做左值
//k6++;常量引用,不能做左值
//k1=667,k2=667,k3=667,k4=889,k5= 888,k6=888
std::cout<<"k1的值为: "<<k1<<" 类型为: "<<typeid(k1).name()<<std::endl;
std::cout<<"k2的值为: "<<k2<<" 类型为: "<<typeid(k2).name()<<std::endl;
std::cout<<"k3的值为: "<<k3<<" 类型为: "<<typeid(k3).name()<<std::endl;
std::cout<<"k4的值为: "<<k4<<" 类型为: "<<typeid(k4).name()<<std::endl;
std::cout<<"k5的值为: "<<k5<<" 类型为: "<<typeid(k5).name()<<std::endl;
std::cout<<"k6的值为: "<<k6<<" 类型为: "<<typeid(k6).name()<<std::endl;
return 0;
}
运行截图:
2.6:自定义数据结构
2.39:编译下面的程序观察其运行结果,注意,如果忘记写类定义体后面的分号会发生什么情况?记录下相关信息,以后可能会有用.
答案:
#include<iostream>
struct Foo{
/*此处为空*/
}
int main()
{
return 0;
}
运行截图:
2.40:根据自己的理解写出Sales_data类,最好与书中的例子有所区别.
答案:见下面代码块儿
#include<iostream>
struct Sale_data{
std::string bookNo;//书籍编号
unsigned int units_sold = 0;//销售量
double sellingprice = 0.0;//零售价
double saleprice = 0.0;//实售价
double discount = 0.0;//折扣
};
int main()
{return 0;}
2.41:使用你自己的Sales_data类重写1.5.1节(第20页),1.5.2节(第21页)和1.6节(第22页的练习).眼下先把Sales_data类的定义和main函数放在同一个文件里.
答案:见下面代码块儿,为了节约篇幅,将所有题的调用方法都写到了主函数,各位在测试的时候,在主函数中把别的内容注释掉,然后测试想测试的内容
#include <iostream>
#include <string>
using namespace std;
class Sales_data
{
friend std::istream &operator>>(std::istream &, Sales_data &);
friend std::ostream &operator<<(std::ostream &, const Sales_data &);
friend bool operator<(const Sales_data &, const Sales_data &);
friend bool operator==(const Sales_data &, const Sales_data &);
public:
Sales_data() = default;
Sales_data(const std::string &book) : bookNo(book) {}
Sales_data(std::istream &is) { is >> *this; }
public:
Sales_data &operator+=(const Sales_data &);
std::string isbn() const { return bookNo; }
private:
std::string bookNo; //书籍编号
unsigned int units_sold = 0; //销售量
double sellingprice = 0.0; //原始价格
double saleprice = 0.0; //实售价
double discount = 0.0; //折扣
};
inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
return lhs.isbn() == rhs.isbn();
}
Sales_data operator+(const Sales_data &, const Sales_data &);
inline bool operator==(const Sales_data &lhs, const Sales_data &rhs)
{
return lhs.units_sold == rhs.units_sold && lhs.sellingprice == rhs.sellingprice && lhs.saleprice == rhs.saleprice && lhs.isbn() == rhs.isbn();
}
inline bool operator!=(const Sales_data &lhs, const Sales_data &rhs)
{
return !(lhs == rhs);
}
Sales_data &Sales_data::operator+=(const Sales_data &rhs)
{
units_sold += rhs.units_sold;
saleprice = (rhs.saleprice * rhs.units_sold + saleprice * units_sold) / (rhs.units_sold + units_sold);
if (saleprice != 0)
discount = saleprice / sellingprice;
return *this;
}
Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data ret(lhs);
ret += rhs;
return ret;
}
std::istream &operator>>(std::istream &in, Sales_data &s)
{
in >> s.bookNo >> s.units_sold >> s.sellingprice >> s.saleprice;
if (in && s.sellingprice != 0)
s.discount = s.saleprice / s.sellingprice;
else
s = Sales_data();
return in;
}
std::ostream &operator<<(std::ostream &out, const Sales_data &s)
{
out << s.isbn() << " " << s.units_sold << " " << s.sellingprice << " " << s.saleprice << " " << s.discount;
return out;
}
int main()
{
Sales_data book;
std::cout << "请输入销售记录: " << std::endl;
while (std::cin >> book)
{
std::cout << "ISBN、售出本书、原始价格、实际价格、折扣为" << book << std::endl;
}
Sales_data trans1, trans2;
std::cout << "请输入两条ISBN相同的销售记录:" << std::endl;
std::cin >> trans1 >> trans2;
if (compareIsbn(trans1, trans2))
std::cout << "汇总信息:ISBN、售出本数、原始价格、实售价格、折扣为 " << trans1 + trans2 << std::endl;
else
std::cout << "两条销售记录的ISBN不同" << std::endl;
Sales_data total, trans;
std::cout << "请输入几条ISBN相同的销售记录:" << std::endl;
if (std::cin >> total)
{
while (std::cin >> trans)
{
if (compareIsbn(total, trans))
total = total + trans;
else
{
std::cout<<"当前书籍ISBN不同"<<std::endl;
break;
}
}
std::cout << "有效汇总信息:ISBN、售出本数、原始价格、实售价格、折扣为" << total << std::endl;
}
else
{
std::cout << "没有数据" << std::endl;
return -1;
}
int num = 1; //记录当前书籍的销售记录总数
std::cout << "请输入若干销售记录:" << std::endl;
if (std::cin >> trans1)
{
while (std::cin >> trans2)
{
if (compareIsbn(trans1, trans2))
num++;
else
{
std::cout << trans1.isbn() << "共有" << num << "条销售记录" << std::endl;
trans1 = trans2;
num = 1;
}
std::cout << trans1.isbn() << "共有" << num << "条销售记录" << std::endl;
}
}
else
{
std::cout<<"没有数据"<<std::endl;
return -1;
}
return 0;
}
2.42:根据自己的理解重写一个Sales_data.h头文件,并以此为基础重做2.6.2节(第67页的练习)
答案:这道题和上一个题没什么区别,鉴于节约篇幅,还是把所有题都放到了一个主函数中,大家测试时把主函数中不相关的注释掉即可.
Sales_data.h
#ifndef SALES_DATA_H_INCLUDED
#define SALES_DATA_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class Sales_data
{
friend std::istream &operator>>(std::istream &, Sales_data &);
friend std::ostream &operator<<(std::ostream &, const Sales_data &);
friend bool operator<(const Sales_data &, const Sales_data &);
friend bool operator==(const Sales_data &, const Sales_data &);
public:
Sales_data() = default;
Sales_data(const std::string &book) : bookNo(book) {}
Sales_data(std::istream &is) { is >> *this; }
public:
Sales_data &operator+=(const Sales_data &);
std::string isbn() const { return bookNo; }
private:
std::string bookNo; //书籍编号
unsigned int units_sold = 0; //销售量
double sellingprice = 0.0; //原始价格
double saleprice = 0.0; //实售价
double discount = 0.0; //折扣
};
inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
return lhs.isbn() == rhs.isbn();
}
Sales_data operator+(const Sales_data &, const Sales_data &);
inline bool operator==(const Sales_data &lhs, const Sales_data &rhs)
{
return lhs.units_sold == rhs.units_sold && lhs.sellingprice == rhs.sellingprice && lhs.saleprice == rhs.saleprice && lhs.isbn() == rhs.isbn();
}
inline bool operator!=(const Sales_data &lhs, const Sales_data &rhs)
{
return !(lhs == rhs);
}
Sales_data &Sales_data::operator+=(const Sales_data &rhs)
{
units_sold += rhs.units_sold;
saleprice = (rhs.saleprice * rhs.units_sold + saleprice * units_sold) / (rhs.units_sold + units_sold);
if (saleprice != 0)
discount = saleprice / sellingprice;
return *this;
}
Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data ret(lhs);
ret += rhs;
return ret;
}
std::istream &operator>>(std::istream &in, Sales_data &s)
{
in >> s.bookNo >> s.units_sold >> s.sellingprice >> s.saleprice;
if (in && s.sellingprice != 0)
s.discount = s.saleprice / s.sellingprice;
else
s = Sales_data();
return in;
}
std::ostream &operator<<(std::ostream &out, const Sales_data &s)
{
out << s.isbn() << " " << s.units_sold << " " << s.sellingprice << " " << s.saleprice << " " << s.discount;
return out;
}
#endif
2.42.cc
#include "Sales_data.h"
int main()
{
Sales_data book;
std::cout << "请输入销售记录: " << std::endl;
while (std::cin >> book)
{
std::cout << "ISBN、售出本书、原始价格、实际价格、折扣为" << book << std::endl;
}
Sales_data trans1, trans2;
std::cout << "请输入两条ISBN相同的销售记录:" << std::endl;
std::cin >> trans1 >> trans2;
if (compareIsbn(trans1, trans2))
std::cout << "汇总信息:ISBN、售出本数、原始价格、实售价格、折扣为 " << trans1 + trans2 << std::endl;
else
std::cout << "两条销售记录的ISBN不同" << std::endl;
Sales_data total, trans;
std::cout << "请输入几条ISBN相同的销售记录:" << std::endl;
if (std::cin >> total)
{
while (std::cin >> trans)
{
if (compareIsbn(total, trans))
total = total + trans;
else
{
std::cout<<"当前书籍ISBN不同"<<std::endl;
break;
}
}
std::cout << "有效汇总信息:ISBN、售出本数、原始价格、实售价格、折扣为" << total << std::endl;
}
else
{
std::cout << "没有数据" << std::endl;
return -1;
}
int num = 1; //记录当前书籍的销售记录总数
std::cout << "请输入若干销售记录:" << std::endl;
if (std::cin >> trans1)
{
while (std::cin >> trans2)
{
if (compareIsbn(trans1, trans2))
num++;
else
{
std::cout << trans1.isbn() << "共有" << num << "条销售记录" << std::endl;
trans1 = trans2;
num = 1;
}
std::cout << trans1.isbn() << "共有" << num << "条销售记录" << std::endl;
}
}
else
{
std::cout<<"没有数据"<<std::endl;
return -1;
}
return 0;
}
已码三万字!第一二章更新完毕:点击链接进入原作
转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
第3章 字符串,向量和数组
3.1 命令空间的using声明
3.1:使用恰当的using声明重做1.4.1节(第11页)和2.6.2节(第67页)的练习
答案:鉴于此题比较简单,而且之前的第二章也都有答案,所以再次就不再赘述,仅列出1.9题为例
#include <iostream>
using std::cout;
using std::endl;
int main()
{
int sum = 0;
for(int i = 50 ; i <= 100 ; i++)
{
sum += i;
}
cout<<"Sum of 50 to 100 inclusive is "<<sum<<endl;
return 0;
}
运行截图:
3.2 标准库类型string
3.2:编写一段程序从标准输入中一次读入一整行,然后修改该程序使其一次读入一个词.
答案:第一个代码块为一次读入一整行,第二个代码块为一次读入一个词(ps:我运行程序时候,直接将一首CountingStars的歌词定重定向作为输入)
#include<iostream>
#include<string>
using std::cin;using std::cout;using std::string;
using std::endl;
int main()
{
string line;
while(getline(cin,line))
{
cout<<line<<endl;
}
return 0;
}
运行截图:
第二个代码块:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string word;
while(cin>>word)
{
cout<<word<<endl;
}
return 0;
}
运行截图:
3.3:请说明string类的输入运算符和getline函数分别是如何处理空白字符的.
答案:
标准库输入运算符自动忽略字符串开头的空白(包括空格符,换行符,制表符等),从第一个真正的字符开始读起,直到遇见下一处空白为止
getline函数从给定的输入流中读取数据,直到遇到换行符为止,此时换行符也被读取进来,但是并不存储在最后的字符串中.
已码三万四千字!第一二章更新完毕:点击链接进入原作
转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
3.4:编写一段程序读入两个字符串,比较其是否相等并输出结果.如果不相等,输出比较大的那个字符串.改写上述程序,比较输入的两个字符串是否等长,如果不等长,输出长度较大的那个字符串
答案:
比较大小的程序如下:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1,s2;
cin>>s1>>s2;
if(s1 == s2)
{
cout<<"两个字符串相等"<<endl;
}
else
{
if(s1 > s2)
cout<<s1<<" 大于 "<<s2<<endl;
else
cout<<s1<<" 小于 "<<s2<<endl;
}
return 0;
}
运行截图:
比较长度大小的代码如下:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1,s2;
cin>>s1>>s2;
if(s1.size() == s2.size())
{
cout<<s1<<" 的长度等于 "<<s2<<" 的长度相等"<<endl;
}
else
{
if(s1.size() > s2.size())
cout<<s1<<" 的长度大于 "<<s2<<" 的长度"<<endl;
else
cout<<s1<<" 的长度小于 "<<s2<<" 的长度"<<endl;
}
return 0;
}
运行截图:
3.5:编写一段程序从标准输入中读入多个字符并将它们连接在一起,输出连接成的大字符串.然后修改上述程序,用空格把输入的多个字符串分隔开来.
答案:
连接多个字符串,输出连接成的大写字符串,见下代码块:
#include <iostream>
#include <string>
#include<cctype>
using namespace std;
int main()
{
cout<<"请输入第一个字符串:"<<endl;
string str1,str2,cont;
while(cin>>str1)
{
str2+=str1;
cout<<"是否要继续进行输入?请输入(y或Y)"<<endl;
cin>>cont;
if(cont == "y" || cont == "Y")
cout<<"请输入下一个字符串:"<<endl;
else
break;
}
for(int i = 0 ; i < str2.size() ; i++)
{
if( str2[i] >= 97 && str2[i] <=122)
str2[i] = str2[i] - 32;
}
cout<<str2<<endl;
return 0;
}
运行截图如下:
用空格把输入的多个字符分隔开来,并输出大写字符串,代码如下:
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout<<"请输入第一个字符串:"<<endl;
string str1,str2,cont;
while(cin>>str1)
{
if(!str2.size())//判断第一个输入的字符串前不加空格
str2 = str1;
else
str2 = str2 + " " + str1;
cout<<"是否要继续进行输入?请输入(y或Y)"<<endl;
cin>>cont;
if(cont == "y" || cont == "Y")
cout<<"请输入下一个字符串"<<endl;
else
break;
}
for(int i = 0 ; i < str2.size() ; i++)
{
if( str2[i] >= 97 && str2[i] <=122)
str2[i] = str2[i] - 32;
}
cout<<str2<<endl;
return 0;
}
运行截图:
3.6:编写一段程序,使用范围for语句将字符串内的所有字符用X代替.
答案:见下面代码(个人觉得初学者应该注意的两点1:auto类型,2:将c设置为引用类型才可以修改s的值)
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s("Hello MrQin");
cout<<"before update s = "<<s<<endl;
for(auto &c : s)
c = 'X';
cout<<"after update s = "<<s<<endl;
return 0;
}
运行截图:
3.7:就上一题完成的程序而言,如果将循环控制变量的类型设为char将发生什么?先估计一下结果,然后实际编程进行验证.
预测:没有影响,因为利用auto推测出的类型也是char
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s("Hello MrQin");
cout<<"before update s = "<<s<<endl;
for(char &c : s)
c = 'X';
cout<<"after update s = "<<s<<endl;
return 0;
}
运行截图:
3.8:分别用while循环和传统的for循环重写第一题的程序,你觉得哪种形式更好呢?为什么?
答案:我们的目的是希望处理字符串中每一个字符,并且不需要在意字符的处理顺序,因此与传统的while循环和for循环相比,使用范围for循环更简洁直观.
1.用while循环:
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout<<"请输入字符串,按Enter键结束"<<endl;
string s;
getline(cin,s);
cout<<"before update s = "<<s<<endl;
int i = 0;
while(s[i] != '\0')
{
auto &c = s[i];
c = 'X';
i++;
}
cout<<"after update s = "<<s<<endl;
return 0;
}
运行截图:
2.用for循环
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout<<"请输入字符串,按Enter键结束"<<endl;
string s;
getline(cin,s);
cout<<"before update s = "<<s<<endl;
int i = 0;
for(int i = 0 ; s[i] != '\0' ; i++)
{
s[i] = 'X';
}
cout<<"after update s = "<<s<<endl;
return 0;
}
运行截图:
3.9:下面的程序有何作用?它合法么?如果不合法?为什么?
string s;
cout<<s[0]<<endl;
答案:不合法,因为该程序的原意是输出字符串s的首字符,但程序是错误的.因为初始状态下没有给s赋任何初值,所以字符串s的内容为空,当然也就不存在首字符,下标0是非法的.但是在某些编译器环境中,上述语句并不会引发编译错误.(比如我的ubuntu环境)
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
cout<<s[0]<<endl;
return 0;
}
运行结果:
3.10:编写一段程序,读入一个包含标点符号的字符串,将标点符号去除后输出字符串的剩余部分.
答案:
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout<<"请输入包含标点符号的字符串,按Enter键结束"<<endl;
string s;
getline(cin,s);
cout<<"before update s:"<<s<<endl;
cout<<"after update s:";
for(auto c : s)
{
if(!ispunct(c))
cout<<c;
}
cout<<endl;
return 0;
}
运行结果:
3.11:下面的范围for语句合法吗?如果合法,c的类型是什么?
const string s = "Keep out!";
for(auto &c :s){/*...*/}
语法上是合法, 但是c的类型为常量引用 , 如果涉及到改变c的值的操作就会报错
#include <iostream>
#include <string>
using namespace std;
int main()
{
const string s = "Keep out!";
for(auto &c : s)
{
c = 'X';
}
return 0;
}
运行结果:
已码三万八千字!第一二章更新完毕:点击链接进入原作
转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
3.3 标准库类型vector
3.12: 下列vector对象的定义有不正确的吗?如果有,请指出来.对于正确的,描述其具体执行结果;对于不正确的,说明其错误的原因.
答案:
vector<vector> ivec; //正确,定义了一个名为ivec的vector对象,其中每个元素都是vector对象
vector svec = ivec;//错误,因为ivec的元素是int,而svec的元素是string,故ivec不能初始化svec
vector svec(10,“null”);//正确,svec有10个值为"null"的元素
#include <string>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<vector<int>> ivec;
//vector<string> svec1 = ivec;
vector<string> svec(10,"null");
for(auto c : svec)
{
cout<<c<<endl;
}
return 0;
}
运行结果:
3.13: 下列的vector对象各包含多少个元素?这些元素的值分别是多少?
答案:
(a):vector v1;//默认初始化,v1不含任何元素
(b):vector v2(10);//10个元素,每个都初始化为0
©:vector v3(10,42);//10个元素,每个都初始化为42
(d):vector v4{10};//列表初始化,1个元素其值为10
(e):vector v5{10,4};//列表初始化,2个元素其值分别为10,4
(f):vector v6{10};//列表初始化,10为int值,类型不匹配,故10当做给定的容量来构造vector对象
(g):vector v7{10,“hi”};//列表初始化,10个元素其值都是hi
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<int> v1;
for(auto a : v1)
cout<<"a="<<a<<endl;
vector<int> v2(10);
for(auto b:v2)
cout<<"b="<<b<<endl;
vector<int> v3(10,42);
for(auto c:v3)
cout<<"c="<<c<<endl;
vector<int> v4{10};
for(auto d:v4)
cout<<"d="<<d<<endl;
vector<int> v5{10,4};
for(auto e:v5)
cout<<"e="<<e<<endl;
vector<string> v6{10};
for(auto f:v6)
cout<<"f="<<f<<endl;
vector<string> v7{10,"hi"};
for(auto g:v7)
cout<<"g="<<g<<endl;
return 0;
}
运行截图:
3.14: 编写一段程序,用cin读入一组整数并把它们存入一个vector对象.
答案:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> ivec;
int a;
char cont = 'y';
cout<<"输入第一个整数:"<<endl;
while(cin >> a)
{
ivec.push_back(a);
cout<<"您要继续么(n or y)?"<<endl;
cin>>cont;
if(cont != 'y' && cont != 'Y')
break;
cout<<"输入下一个整数:"<<endl;
}
cout<<endl<<"输入的整数为:";
for(auto b:ivec)
{
cout<<b<<" ";
}
cout<<endl;
return 0;
}
运行截图:
3.15: 改写上题的程序,不过这次读入的是字符串
答案:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
string text;
vector<string> svec;
char cont = 'y';
cout<<"请输入第一个字符串:"<<endl;
while(cin>>text)
{
svec.push_back(text);
cout<<"您要继续么(n or y)?"<<endl;
cin>>cont;
if(cont != 'y' && cont!= 'Y')
break;
cout<<"请输入下一个字符串:"<<endl;
}
cout<<"输入的全部字符串为:";
for(auto c : svec)
{
cout<<c<<" ";
}
cout<<endl;
return 0;
}
运行截图:
3.16: 编写一段程序,把练习3.13中的vector对象的容量和具体内容输出出来.检验你之前的回答是否正确,如果不对,回过头重新学习3.3.1节(第87页)直到弄明白错在何处为止
答案:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<int> v1;
cout<<"v1.size = "<<v1.size()<<endl;
for(auto a : v1)
cout<<"v1="<<a<<endl;
vector<int> v2(10);
cout<<"v2.size= "<<v2.size()<<endl;
for(auto b:v2)
cout<<"v2="<<b<<endl;
vector<int> v3(10,42);
cout<<"v3.size= "<<v3.size()<<endl;
for(auto c:v3)
cout<<"v3="<<c<<endl;
vector<int> v4{10};
cout<<"v4.size= "<<v4.size()<<endl;
for(auto d:v4)
cout<<"v4="<<d<<endl;
vector<int> v5{10,4};
cout<<"v5.size= "<<v5.size()<<endl;
for(auto e:v5)
cout<<"v5="<<e<<endl;
vector<string> v6{10};
cout<<"v6.size= "<<v6.size()<<endl;
for(auto f:v6)
cout<<"v6="<<f<<endl;
vector<string> v7{10,"hi"};
cout<<"v7.size= "<<v7.size()<<endl;
for(auto g:v7)
cout<<"v7="<<g<<endl;
return 0;
}
运行截图:
3.17: 从cin读入一组词并把它们存入一个vector对象,然后设法把所有词都改写为大写形式.输出改变后的结果,每个占一行.
答案:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<string> svec;
string text;
char cont = 'y';
cout<<"请输入第一个词:"<<endl;
while(cin >> text)
{
svec.push_back(text);
cout<<"您要继续吗(y or n)?"<<endl;
cin >> cont;
if(cont != 'y' && cont != 'Y' )
break;
cout<<"请输入下一个词:"<<endl;
}
for(auto &a:svec)
{
for(auto &b:a)
b = toupper(b);
}
for(auto c:svec)
{
cout<<c<<endl;
}
return 0;
}
运行截图:
3.18: 下面的程序合法吗?如果不合法,你准备如何修改?
vector<int> ivec;
ivec[0] = 42;
答案:
不合法,因为ivec是一个空vector,根本不包含任何元素,当然也就不能通过下标去访问任何元素!
修改方法为使用ivec.push
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> ivec;
//ivec[0] = 42;
ivec.push_back(42);
for(auto a:ivec)
{
cout<<a<<endl;
}
return 0;
}
运行截图:
3.19: 如果想定义一个含有10个元素的vector对象,所有元素都是42,请列举出三种不同的实现方法.哪种方法最好呢?为什么?
答案:
三种方法见代码块,当vector对象的元素量较多且取值重复时是最好的选择,而不确定元素的个数时第三种方法比较好
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
//第一种:
vector<int> a {42,42,42,42,42,42,42,42,42,42};
//第二种:
vector<int> b (10,42);
//第三种:
vector<int> c;
for(decltype(a.size()) i = 0 ; i < 10 ; i++)
{
c.push_back(42);
}
cout<<"a:";
for(auto d:a)
cout<<d<<" ";
cout<<endl<<"b:";
for(auto e:b)
cout<<e<<" ";
cout<<endl<<"c:";
for(auto f:c)
cout<<f<<" ";
cout<<endl;
return 0;
}
运行截图:
3.20: 读入一组整数并把它们存入一个vector对象,将每对相邻整数的和输出出来.改写你的程序,这次要求先输出第1个和最后1个元素的和,接着输出第2个和倒数第2个元素的和,以此类推.
答案:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<int> ivec;
int temp;
char cont = 'y';
cout<<"请输入第一个整数:"<<endl;;
while(cin >> temp)
{
ivec.push_back(temp);
cout<<"是否要继续输入(n or y)?"<<endl;
cin >> cont;
if(cont != 'y' && cont != 'Y')
break;
cout<<"请输入下一个整数:"<<endl;
}
cout<<"原数列为:";
for(auto a:ivec)
{
cout<<a<<" ";
}
cout<<endl<<"输出每对相邻整数的和:"<<endl;
for(decltype(ivec.size()) i = 0 ; i < ivec.size() - 1 ; i ++)
{
cout<<ivec[i]+ivec[++i]<<" ";
}
if(ivec.size() % 2 == 1)
cout<<ivec[ivec.size() -1];
cout<<endl<<"输出首尾两项的和:"<<endl;
for(decltype(ivec.size()) i = 0 ; i < ivec.size()/2; i++ )
{
cout<<ivec[i] + ivec[ivec.size() - i - 1]<<" ";
}
if(ivec.size() % 2 == 1)
cout<<ivec[ivec.size()/2];
cout<<endl;
return 0;
}
运行截图:
已码四万五千字!第3.3节更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
3.4 迭代器介绍
3.21: 请使用迭代器重做3.3.3节(第94页)的第一个练习.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<int> v1;
cout<<"v1的元素个数是:"<<v1.size()<<endl;
if(v1.cbegin() != v1.cend())
{
for(auto it = v1.cbegin(); it != v1.cend();it++)
cout<<"v1:"<<(*it)<<endl;
}
vector<int> v2(10);
cout<<"v2的元素个数是:"<<v2.size()<<endl;
if(v2.cbegin() != v2.cend())
{
for(auto it = v2.cbegin(); it != v2.cend();it++)
cout<<"v2:"<<(*it)<<endl;
}
vector<int> v3(10,42);
cout<<"v3的元素个数是:"<<v3.size()<<endl;
if(v3.cbegin() != v3.cend())
{
for(auto it = v3.cbegin(); it != v3.cend();it++)
cout<<"v3:"<<(*it)<<endl;
}
vector<int> v4{10};
cout<<"v4的元素个数是:"<<v4.size()<<endl;
if(v4.cbegin() != v4.cend())
{
for(auto it = v4.cbegin(); it != v4.cend();it++)
cout<<"v4:"<<(*it)<<endl;
}
vector<int> v5{10,4};
cout<<"v5的元素个数是:"<<v5.size()<<endl;
if(v5.cbegin() != v5.cend())
{
for(auto it = v5.cbegin(); it != v5.cend();it++)
cout<<"v5:"<<(*it)<<endl;
}
vector<string> v6{10};
cout<<"v6的元素个数是:"<<v6.size()<<endl;
if(v6.cbegin() != v6.cend())
{
for(auto it = v6.cbegin(); it != v6.cend();it++)
cout<<"v6:"<<(*it)<<endl;
}
vector<string> v7{10,"hi"};
cout<<"v7的元素个数是:"<<v7.size()<<endl;
if(v7.cbegin() != v7.cend())
{
for(auto it = v7.cbegin(); it != v7.cend();it++)
cout<<"v7:"<<(*it)<<endl;
}
return 0;
}
运行截图:
3.22: 修改之前那个输出text第一段的程序,首先把text的第一段全都改成大写形式,然后再输出它
#include <string>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<string> text;
string s;
while(getline(cin,s))//getline可以保留输入时的空白符
{
text.push_back(s);
}
for(auto it = text.begin() ; it != text.end() && !(*it).empty();it++)
{
for(auto a = it->begin() ; a != it->end() ; a++)
{
(*a) = toupper(*a);
}
}
for(auto it = text.begin() ; it != text.end() && !(*it).empty();it++)
cout<<(*it);
cout<<endl;
return 0;
}
运行截图:
3.23: 编写一段程序,创建一个含有10个整数的vector对象,然后使用迭代器将所有元素的值都变成原来的两倍.输出vector对象的内容,检验程序是否正确
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
vector<int> Vint;
srand((unsigned)time(NULL));
for(int i = 0 ; i < 10 ; i++)
{
Vint.push_back(rand()%1000);
}
cout<<"随机生成的数列为:";
for(auto it = Vint.begin() ; it != Vint.end() ; it++)
cout<<*it<<" ";
cout<<endl;
cout<<"翻两倍后的数列为:";
for(auto it = Vint.begin() ; it != Vint.end() ; it ++)
{
(*it) *= 2;
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
运行截图:
3.24: 请使用迭代器重做3.3.3节(第94页)的最后一个练习
#include <iostream>
#include <vector>
using namespace std;
int main()
{
cout<<"请输入一组整数,按Ctrl+D结束"<<endl;
int i = 0;
vector<int> Vint;
while(cin >> i)
{
Vint.push_back(i);
}
if(Vint.begin() == Vint.end())
{
cout<<"没有输入任何元素"<<endl;
return -1;
}
cout<<endl<<"输入的整数数列为:"<<endl;
for(auto it = Vint.begin() ; it != Vint.end() ; it++)
{
cout<<*it<<" ";
}
cout<<endl;
cout<<"输出每对相邻整数的和:";
for(auto it = Vint.cbegin() ; it != Vint.cend() - 1 ; it ++)
{
cout<<(*it + *(++it))<<" ";
if(it == Vint.end() -1 )
break;
}
if(Vint.size() % 2 == 1)
{
cout<<*(Vint.end() - 1);
}
cout<<endl;
cout<<"输出首尾元素之和:";
for(auto it = Vint.begin();it !=( Vint.begin()+(Vint.size() / 2)) ; it++)
cout<<*it + *(Vint.begin()+(Vint.end() - it -1))<<" ";
if(Vint.size()%2 == 1)
{
cout<<*(Vint.begin() + (Vint.size() / 2));
}
cout<<endl;
return 0;
}
运行截图:
3.25: 3.3.3节(第93页)划分分数段的程序是使用下标运算符实现的,请利用迭代器改写该程序并实现完全相同的功能
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> Vint(11,0);
cout<<"请输入成绩(0-100),按ctrl+d结束:"<<endl;
int grade;
while(cin>>grade)
{
if(grade < 101)
{
(*(Vint.begin()+grade/10))++;
}
}
cout<<endl<<"一共输入了"<<Vint.size()<<"个成绩"<<endl;
cout<<"各个成绩段的人数从低到高排列分别为"<<endl;
for(auto it = Vint.begin() ; it != Vint.end() ; it++)
{
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
运行截图:
3.26: 在100页的二分搜索程序中,为什么用的是mid=beg+(end-beg)/2,而非mid = (beg+end)/2;?
答案:
mid = beg + (end - begin) / 2 ;
先计算end-beg的值得到容器中的元素个数,然后控制迭代器从开始处向右移动二分之一容器的长度,从而定位到容器正中间的元素
mid = (begin+end)/2;
因为C++中并没有定义两个迭代器的加法运算,因此直接把两个迭代器加起来是没有意义的.
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<string> text{"across","bold","counting","dollars","everying","find","heart","in","just","kill"};
for(auto it : text)
cout<<it<<" ";
cout<<endl;
auto beg = text.begin();
auto end = text.end();
auto mid = beg +(end - beg)/2;
cout<<"请输入要查询的单词,按enter结束:"<<endl;
string str;
cin>>str;
while(str != *mid && mid != end)
{
if(str < *mid)
end = mid;
else
beg = mid+1;
mid = beg +(end - beg)/2;
}
if(mid == end)
cout<<"没有找到"<<endl;
else
{
cout<<str<<"位于"<<*mid<<endl;
}
return 0;
}
运行截图:
已码五万字!第3.4节更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
3.5 数组
3.27:假设txt_size是一个无参数的函数,它的返回值是int.请回答下列哪个定义是非法的?为什么?
(a) int ia[buf_size]; (b) int ia[4*7-14];
(c) int ia[txt_size()]; (d) char st[11] = "fundamental";
答案:见代码块中的注释
#include <iostream>
using namespace std;
int txt_size()
{
int a = 666;
return a;
}
int main()
{
unsigned buf_size = 1024;
//int ia[buf_size];//ia是非法的,buf_size只是一个普通的无符号数,不是一个常量,不能作为数组的维度
int ia1[4*7-14];
//int ia2[txt_size()];//ia2是非法的,txt_size()是一个普通的整型数,不是一个常量,不能作为数组的维度
//char st[11] = "fundamental";字符串结尾处还有一个空字符,没有空间存放空字符
//ps:在一些编译器中(a)和(c)可能别判定为合法,这是由于编译器扩展,比我的这套环境,这个就是合法的
//不过一般来说,大家应该尽量避免使用非标准特性,因为非标准特性可能会在其他编译器中失效.
return 0;
}
3.28:下列元素中的值是什么?
答案:见代码块
/*
sa和ia的定义位置在函数体外,因此会执行默认初始化,sa为空,ia为0
因为string类型本身接受无参数的初始化方式,所以即便定义在函数内部也被默认初始化为空串
ia2的定义位置在函数内,因此不会执行默认初始化,一个未被初始化的内置类型
变量的值是未定义的,如果试图拷贝或输出未初始化的变量,将遇到未定义的奇异值
*/
#include <iostream>
using namespace std;
string sa[10];
int ia[10];
int main()
{
string sa2[10];
int ia2[10];
cout<<"sa:";
for(int i = 0 ; i < 10 ; i ++)
cout<<sa[i]<<" ";
cout<<endl<<"ia:";
for(int i = 0 ; i < 10 ; i ++)
cout<<ia[i]<<" ";
cout<<endl<<"sa2:";
for(int i = 0 ; i < 10 ; i ++)
cout<<sa2[i]<<" ";
cout<<endl<<"ia2:";
for(int i = 0 ; i < 10 ; i ++)
cout<<ia2[i]<<" ";
cout<<endl;
return 0;
}
运行结果:
3.29:相对比vector来说,数组有哪些缺点,请列举一些.
答案:
1.数组与vector相似之处是都能存放类型相同的对象,且这些对象本身没有名字,需要通过其所在位置访问.
2.数组与vector不同,数组的大小固定不变,不能随意向数组中增加额外的元素,虽然在某些情境下运行时性能较好,但是与vector相比损失了灵活性.
3.数组的维度在定义时已经确定,如果我们想更改数组的长度,只能创建一个更大的新数组,然后把原数组的所有元素复制到新数组中.我们也无法像vector那样使用size函数直接获取数组的长度;如果是其他数组,只能使用sizeof(array)/sizeof(array[0])的方式计算数组的维度.
3.30:指出下列代码中的索引错误.
答案:数组的大小为10,索引应该为0-9,不能等于10,故应该是ix<array_size
#include<iostream>
using namespace std;
int main()
{
constexpr size_t array_size = 10;
int ia[array_size];
for(size_t ix = 1; ix <= array_size ; ++ix)
{
ia[ix] = ix;
}
return 0;
}
运行截图:
3.31:编译一段程序,定义一个含有10个int的数组,令每个元素的值就是其下标值.
答案:见代码块
#include <iostream>
using namespace std;
int main()
{
constexpr size_t array_size = 10;
int array[array_size];
for(size_t ix = 0 ; ix < array_size ; ix++)
{
array[ix] = ix;
}
for(auto a : array)
cout<<a<<" ";
cout<<endl;
return 0;
}
运行截图:
3.32:将上一题刚刚创建的数组拷贝给另外一个数组.利用vector重写程序,实现类似的功能.
答案:见代码块
#include <iostream>
#include <vector>
using namespace std;
int main()
{
constexpr size_t array_size = 10;
int array[array_size],array_2[array_size];
for(size_t ix = 0 ; ix < array_size ; ix++)
{
array[ix] = ix;
}
for(size_t ix = 0 ; ix < array_size ; ix++)
{
array_2[ix] = array[ix];
}
cout<<"array:";
for(auto a : array_2)
cout<<a<<" ";
cout<<endl;
vector<int> vec;
for(size_t ix = 0; ix < 10 ;ix++)
vec.push_back(ix);
vector<int> vec_2;
for(size_t ix = 0; ix < 10 ; ix++)
vec_2.push_back(vec[ix]);
cout<<"vector:";
for(auto b: vec_2)
cout<<b<<" ";
cout<<endl;
return 0;
}
运行截图:
3.33:对于104页的程序来说,如果不初始化scores将发生什么?
答案:如果不初始化,因为该代码位于函数内因此不会执行默认初始化,因此该数组会有未定义的值(验证见代码块)
#include<iostream>
using namespace std;
int main()
{
unsigned scores[11];
unsigned grade;
while(cin >> grade)
{
++scores[grade/10];
}
cout<<endl;
for(auto a : scores)
{
cout<<a<<" ";
}
cout<<endl;
return 0;
}
运行截图:
3.34:假定p1和p2指向同一个数组中的元素,则下面程序的功能是什么?什么情况下该程序是非法的?
答案:
p1 += p2 - p1;等价为:p1 = p1 + (p2 - p1);其实指针并没有改变,还是指向原来的位置.
当p1和p2指向不同类型数组中的元素时,该程序是非法的(验证见代码块)
#include <iostream>
using namespace std;
int main()
{
constexpr size_t array_size = 5;
int array[array_size] = {1,2,3,4,5};
int *p1 = array,*p2 = array;
cout<<"p1:"<<*p1<<endl;
p1 += p2 - p1;
cout<<"p1:"<<*p1<<endl;
return 0;
}
运行截图:
3.35:编写一段程序,利用指针将数组中的元素置为0.
答案见代码块
#include<iostream>
using namespace std;
int main()
{
constexpr size_t array_size = 10;
int array[array_size] = {1,1,1,1,1,1,1,1,1,1};
int* beginP = begin(array),*endP = end(array);
for(beginP ; beginP != endP ; beginP++)
*beginP = 0;
for(auto a : array)
cout<<a<<" ";
cout<<endl;
return 0;
}
运行截图:
3.36:编写一段程序,比较两个数组是否相等.再写一段程序,比较两个vector对象是否相等
答案:下面的这段程序的思路是先让一个数组和vector由随机数填充,另一个数组和vector由手动输入.
#include<iostream>
#include <vector>
#include <ctime>
using namespace std;
int main()
{
const int sz = 5;
int array_1[5] ;
int array_2[5] ;
vector<int> vec_1;
vector<int> vec_2;
cout<<"---------------------采用数组开始比较-----------------------"<<endl;
srand(time(NULL));
for(int i = 0 ; i < sz ; i++)
{
array_1[i] = rand()%10;
vec_1.push_back(rand()%10);
}
cout<<"系统数据已经生成,请输入您猜测的5个数字(0~9),可以重复,按回车键结束:"<<endl;
for(int i = 0 ; i < sz ; i++)
if(cin>>array_2[i]);
cout<<"系统数据为:";
for(auto a: array_1)
cout<<a<<" ";
cout<<endl<<"猜测数据为:";
for(auto a: array_2)
cout<<a<<" ";
cout<<endl;
auto beginA = begin(array_1),beginB = begin(array_2);
while(*beginA == *beginB && beginA != end(array_1) && beginB != end(array_2))
{
beginA++,beginB++;
}
if(beginA == end(array_1) && beginB == end(array_2))
cout<<"恭喜您猜对了"<<endl;
else
cout<<"您的猜测有误"<<endl;
cout<<"---------------------采用vector开始比较-----------------------"<<endl;
cout<<"系统数据已经生成,请输入您猜测的5个数字(0~9),可以重复,按回车键结束:"<<endl;
int temp;
for(int i = 0 ; i < sz ; i++)
{
if(cin>>temp)
vec_2.push_back(temp);
}
for(auto a: vec_2)
cout<<a<<" ";
cout<<endl;
auto p =begin(vec_1),q=begin(vec_2);
while(p!=end(vec_1) && q!=end(vec_2))
{
if(*p != *q)
{
cout<<"您的猜测有误"<<endl;
return -1;
}
p++;q++;
}
cout<<"恭喜您又猜对了"<<endl;
return 0;
}
运行截图:
3.37:下面的程序是何含义,程序的输出结果是什么?
答案:正常情况下,ca的5个字符全部输出后,并没有遇到预期的空字符,也就是while的循环条件仍然满足,无法跳出,程序继续在内存中ca的存储位置之后挨个寻找空字符,直到找到为止,这个过程中额外的内容也会被输出出来,从而产生错误,笔者的机器有编译器拓展功能,规避了这个问题(验证见代码块)
#include <iostream>
using namespace std;
int main()
{
const char ca[] = {'h','e','l','l','o'};
const char *cp = ca;
while(*cp)
{
cout<<*cp<<endl;
++cp;
}
return 0;
}
运行截图:
3.38:在本节中我们提到,将两个指针相加不但是非法的,而且也没什么意义.请解释为什么两个指针相加没什么意义?
答案:
指针也是一个对象,与指针相关的属性有3个,分别是指针本身的值,指针所指的对象以及指针本身在内存中的存储位置.他们的含义分别是:
指针本身的值是一个内存地址值,表示指针所指对象在内存中的存储地址;
指针所指的对象可以通过解引用指针访问;
因为指针也是一个对象,所以指针也存储在内存的某个位置,它有自己的地址,这也是为什么有"指针的指针"的原因
通过上述分析我们知道,指针的值是它所指对象的内存地址,如果我们把两个指针加在一起,就是试图把内存中两个对象的存储地址加在一起,这显然是没有任何意义的,与之相反,指针的减法是有意义的,如果两个指针指向同一个数组中的不同元素,则他们相减的结果表征了他们所指的元素在数组中距离.
3.39:编写一段程序,比较两个string对象.再编写一段程序,比较两个C风格字符串的内容
答案:见代码块
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
cout<<"请输入两个字符串:"<<endl;
string str1,str2;
cin>>str1>>str2;
char ca[80];
char pa[80];
if(str1 > str2)
cout<<"第一个字符串大于第二个字符串"<<endl;
else if(str1 < str2)
cout<<"第一个字符串小于第二个字符串"<<endl;
else
cout<<"两个字符串相等"<<endl;
cout<<"请继续输入两个字符串"<<endl;
cin>>ca>>pa;
auto result = strcmp(ca,pa);
if(result == 0)
cout<<"两个字符串相等"<<endl;
else if(result > 0)
cout<<"第一个字符串大于第二个字符串"<<endl;
else
cout<<"第一个字符串小于第二个字符串"<<endl;
return 0;
}
运行截图:
3.40 编写一段程序,定义两个字符数组并用字符串字面值初始化它们:接着再定义一个字符数组存放前两个数组拼接后的结果.使用strcpy和strcat把前两个数组的内容拷贝到第三个数组中.
答案:见代码块
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char pa[] = {'h','e','l','l','o',' ','\0'};
char pb[] = {'w','o','r','l','d','\0'};
char pc[strlen(pa)+strlen(pb)];
strcpy(pc,pa);
strcat(pc,pb);
cout<<"第一个字符串是:"<<pa<<endl;
cout<<"第二个字符串是:"<<pb<<endl;
cout<<"拼接后的字符串是:"<<pc<<endl;
return 0;
}
运行截图:
3.41:编写一段程序,用整型数组初始化一个vector对象
答案:见代码块
#include<iostream>
#include<vector>
using namespace std;
int main()
{
const int sz = 5;
int ia[sz];
srand((unsigned)time(NULL));
for(auto i = 0 ; i < sz ; i++)
{
ia[i] = rand()%10;
}
vector<int> Vint(begin(ia),end(ia));
for(auto a: Vint)
cout<<a<<" ";
cout<<endl;
return 0;
}
运行截图:
3.42:编写一段程序,将含有整数元素的vector对象拷贝给一个整型数组
答案:见代码块
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> Vint;
srand((unsigned)time(NULL));
const int sz = 10;
for(int i =0 ; i < sz ; i++)
Vint.push_back(rand()%100);
int ia[Vint.size()];
auto it = Vint.cbegin();
for(auto &val : ia)
{
val = *it;
it++;
}
for(auto a: ia)
cout<<a<<" ";
cout<<endl;
return 0;
}
运行截图:
3.6 多维数组
3.43:编写3个不同版本的程序,令其均能输出ia的元素.版本1使用范围for语句管理迭代过程,版本2和版本3都使用普通的for语句,其中版本2要求用下标运算符,版本3要求用指针.此外,在所有3个版本的程序中都要直接写出数据类型,而不能使用类型别名,auto关键字或decltype关键字.
答案:见代码块
#include<iostream>
using namespace std;
int main()
{
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
cout<<"版本1:"<<endl;
for(int (&row)[4]:ia)
{
for(int col:row)
cout<<col<<" ";
cout<<endl;
}
cout<<"版本2:"<<endl;
for(int row = 0 ; row != 3 ; row++)
{
for(int col = 0 ; col != 4 ; col++)
cout<<ia[row][col]<<" ";
cout<<endl;
}
cout<<"版本3:"<<endl;
for(int (*row)[4] = ia ; row != ia + 3 ; row++)
{
for(int* col = *row ; col != *row +4 ;col++)
cout<<*col<<" ";
cout<<endl;
}
return 0;
}
运行截图:
3.44:改写上一个练习中的程序,使用类型别名来代替循环控制变量中的类型
答案:见代码块
#include<iostream>
using namespace std;
using int_array = int[4];
int main()
{
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
cout<<"版本1:"<<endl;
for(int_array &col : ia)
{
for(int row:col)
cout<<row<<" ";
cout<<endl;
}
cout<<"版本2:"<<endl;
for(int row = 0 ; row != 3 ; row++)
{
for(int col = 0 ; col != 4 ; col++)
cout<<ia[row][col]<<" ";
cout<<endl;
}
cout<<"版本3:"<<endl;
for(int_array *row = ia ; row != ia + 3 ; row ++)
{
for(int* col = *row ; col != (*row) +4 ; col++)
cout<<*col<<" ";
cout<<endl;
}
return 0;
}
运行截图:
3.45:再一次改写程序,这次用auto关键字
答案:见代码块儿
#include <iostream>
using namespace std;
int main()
{
int ia[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
cout<<"版本1:"<<endl;
for(auto &row:ia)
{
for(auto col:row)
cout<<col<<" ";
cout<<endl;
}
cout<<"版本2:"<<endl;
for(auto row = 0 ; row < 3 ; row++)
{
for(auto col = 0 ; col < 4 ; col++)
cout<<ia[row][col]<<" ";
cout<<endl;
}
cout<<"版本3:"<<endl;
for(auto row = ia ; row != ia +3 ; row++)
{
for(auto col = *row ; col != *row +4 ; col++)
cout<<*col<<" ";
cout<<endl;
}
return 0;
}
运行截图:
已码六万字!第三章更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
第4章 表达式
4.1 基础
4.1:表达式5+10*20/2的求值结果是多少?
答案:105
#include<iostream>
using namespace std;
int main()
{
cout<<5+10*20/2<<endl;
return 0;
}
运行截图:
4.2:根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致.
答案:见代码块
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> vec;
srand((unsigned)time(NULL));
const int sz = 5;
for(auto i = 0 ; i < sz ; i++)
{
vec.push_back(rand()%10);
}
cout<<*vec.begin()<<endl;
cout<<*(vec.begin())<<endl;
cout<<*vec.begin()+1<<endl;
cout<<((*(vec.begin()))+1)<<endl;
return 0;
}
运行截图:
4.3:C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地.这种策略实际上是代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由.
答案:
C++只规定了非常少的二元运算符(逻辑与运算符,逻辑或运算符,逗号运算符)的求值顺序,其绝大多数二元运算符的求值顺序并没有明确规定这样做提高了代码生成的效率,但是可能引发潜在的缺陷.
关键是缺陷的风险有多大?我们知道,对于没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为;而如果运算对象彼此无关,它们既不会改变同一对象的状态也不执行IO任务,则函数的调用顺序不受限制
这样的做法在一定程度上可以接受的,前提是在编写程序时注意以下两点:一是拿不准的时候最好用括号来强制让表达式的组合关系符合程序逻辑的要求;二是一旦改变了某个运算对象的值,在表达式的其他地方就不要再使用这个运算对象了
Markdown已码6.1万字!第4.1章更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
4.2 算术运算符
4.4:在下面的表达式中添加括号,说明其求值的过程及最终结果.编写程序编译该(不加括号)表达式并输出其结果验证之前的推断.
答案:见代码块儿
#include<iostream>
using namespace std;
int main()
{
cout<<12/3*4+5*15+24%4/2<<endl;
cout<<((12/3)*4)+(5*15)+((24%4)/2)<<endl;//16+75=91
return 0;
}
运行截图:
4.5:写出下列表达式的求值结果.
答案:见代码块儿
#include<iostream>
using namespace std;
int main()
{
cout<<-30*3+21/5<<endl;//-90+4=-86
cout<<-30+3*21/5<<endl;//-30+63/5=-30+12=-18
cout<<30/3*21%5<<endl;//10*21%5=210%5=0
cout<<-30/3*21%4<<endl;//-10*21%4=-210%4=-2
return 0;
}
运行截图:
4.6:写出一条表达式用于确定一个整数是奇数还是偶数.
答案:见代码块儿
#include<iostream>
using namespace std;
int main()
{
int m;
char flag = 'y';
while (1)
{
cout<<"请输入你要判断的数:";
cin>>m;
if(m%2 == 0)
cout<<"m是偶数"<<endl;
else
cout<<"m是奇数"<<endl;
cout<<"是否继续进行判断(y or n)?"<<endl;
cin>>flag;
if(flag != 'y' && flag != 'Y')
break;
}
return 0;
}
运行截图:
4.7: 溢出是何含义?写出三条将导致溢出的表达式.
答案:
溢出是一种常见的算术运算的错误.因为在计算机中存储某种类型的内存空间有限,所以该类型的表示能力(范围)也是有限的,当计算的结果值超出这个范围时,就会产生未定义的数值,这种错误称为溢出
三条将导致溢出的表达式见代码块:
#include<iostream>
using namespace std;
int main()
{
int i = 2147483647 + 1;
int j = -100000 * 300000;
int k = 2015*2015*2015*2015;
cout<<i<<endl;
cout<<j<<endl;
cout<<k<<endl;
return 0;
}
运行截图:
Markdown已码6.3万字!第4.2章更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
4.3 逻辑和关系运算符
4.8:说明在逻辑与、逻辑或及相等性运算符中运算对象求值的顺序.
答案:
对于逻辑与运算符来说,当且仅当两个运算对象都为真时结果为真;对于逻辑或运算符来说,只要两个运算对象中的一个为真结果为真.
逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值.这种策略就是短路求值.其策略是:对于逻辑与运算符来说,当且仅当左侧运算对象为真时才计算右侧运算对象的值;对于逻辑或运算符来说,当且仅当左侧运算对象为假时才计算右侧运算对象.
逻辑与运算符和逻辑或运算符是C++中仅有的几个规定了的求值顺序的运算符.相等性运算符的两个对象都需要求值,C++没有规定其求值顺序.
4.9:解释在下面的if语句中条件部分的判断过程.
答案:
cp是指向字符串的指针,因此上式条件部分含义是首先检查指针cp是否有效.如果cp为空指针或无效指针则条件不满足.如果cp有效,即cp指向了内存中的某个有效地址,继续解引用指针cp并检查cp所指的对象是否为空字符’\0’,如果cp所指的对象不是空字符则条件满足;否则不满足.
#include <iostream>
using namespace std;
int main()
{
const char* cp = "Hello World";
const char*p = NULL;
if(cp&&*cp)
{
cout<<"cp&&*cp"<<endl;
}
return 0;
}
运行截图:
4.10:为while循环写一个条件,使其从标准输入中读取整数,遇到42时停止.
答案:见代码块儿
#include<iostream>
using namespace std;
int main()
{
int temp;
while(cin>>temp&&temp != 42);
cout<<"退出输入"<<endl;
return 0;
}
运行截图:
4.11:书写一条表达式用于测试4个值a,b,c,d的关系,确保a大于b,b大于c,c大于d.
答案:见代码块
#include<iostream>
using namespace std;
int main()
{
int a,b,c,d;
cout<<"分别输入a,b,c,d(a>b,b>c,c>d)的值:";
cin>>a>>b>>c>>d;
while(a<=b||b<=c||c<=d)
{
cout<<"输入错误,请重新输入"<<endl;
cin>>a>>b>>c>>d;
}
cout<<"输入成功"<<endl;
return 0;
}
运行截图:
4.12:假设i,j和k是三个整数,说明表达式(i !=j小于k)的含义
答案:因为<的优先级要比!=高,故先对j<k进行运算得到布尔值0或1;然后将整数i的值与0或1进行比较,如果相等则得到布尔值1,不等则得到布尔值0
#include<iostream>
using namespace std;
int main()
{
int i,j,k;
char temp = 'y';
while(1)
{
cout<<"请输入i,j,k的值"<<endl;
cin>>i>>j>>k;
cout<<"i!=j<k:"<<(i!=j<k)<<endl;
cout<<"是否继续输入(y or n)"<<endl;
cin>>temp;
if(temp != 'y')
break;
}
return 0;
}
运行截图:
Markdown已码6.5万字!第4.3章更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
4.4 赋值运算符
4.13:在下面语句中,当赋值完成后i和d的值分别是多少?
答:
int i;double d;
d = i = 3.5;//i = 3; d = 3;
i = d =3.5;//i = 3; d = 3.5;
#include <iostream>
using namespace std;
int main()
{
int i;double d;
d = i = 3.5;
cout<<" d : "<<d<<", i : "<<i<<endl;
i = d = 3.5;
cout<<" d : "<<d<<", i : "<<i<<endl;
return 0;
}
运行截图:
4.14: 执行下述if语句后将发生什么情况?
if(42 = i)//…
if(i = 42)//…
答:
第一条语句发生编译错误,因为赋值运算符的左侧运算对象必须是左值,字面值常量42显然不是左值,不能作为左侧运算对象
第二条语句从语法上来说是正确的,但是与程序的原意不符.程序的原意是判断i的值是否是42,应该写成i==42;而i=42的意思是把42赋值给i,然后判断i的值是否为真.因为所有非0整数转换成布尔值时都对应true,所以该条件是恒为真的.
#include <iostream>
using namespace std;
int main()
{
int i = 8;
//if(42 = i);
if(i = 42)
cout<<"if(i = 42) : true"<<endl;
else
cout<<"if(i = 42) : false"<<endl;
return 0;
}
运行截图:
4.15:下面的赋值是非法的,为什么?应该如何修改?
double dval;int ival; int *pi;
dval = ival = pi = 0;
答:
该赋值语句是非法的,虽然连续赋值的形式本身没有错,但是参与赋值的几个变量类型不同.其中,dval是双精度浮点数,ival是整数,pi是整数指针
自右向左分析赋值操作的意义,pi=0表示pi是一个空指针,接下来ival=pi试图把整型指针的值赋给整数,这是不符合语法规范的操作,无法编译通过.修改方式见下代码块:
#include<iostream>
using namespace std;
int main()
{
double dval;int ival; int *pi;
dval = ival = 0 ;
pi = 0;
cout<<"pi:"<<pi<<endl;
cout<<"ival:"<<ival<<endl;
cout<<"dval:"<<dval<<endl;
return 0;
}
运行截图:
4.16:尽管下面的语句合法,但它们实际执行的行为可能和预期并不一样,为什么?应该如何修改?
(a) if(p = getPtr() != 0)
(b) if(i = 1024)
答:
(a)的原意是把getPtr()得到的指针赋值给p,然后判断p是否是一个空指针,但是上述表达式的实际执行效果与之相差甚远.因为赋值运算符的优先级低于不相等运算符,所以真正的表达式求职过程是先判断getPtr()的返回值是否是空指针,如果是则p=0,否则p=1,最后以p的值作为if语句的条件.
(b)的原意是判断i的值是否是1024,但上述表达式实际上是把1024赋值给i,然后以i作为if语句的条件.因为所有非0整数换成布尔值都对应为true,所以该条件是真的
修改见下代码块:
#include <iostream>
using namespace std;
int* getPtr()
{
int ival = 0;
int *pi = &ival;
return pi;
}
int main()
{
int *p;
int i = 1023;
if((p = getPtr()) !=0)
cout<<"ture"<<endl;
else
cout<<"false"<<endl;
if( i == 1024)
cout<<"ture"<<endl;
else
cout<<"false"<<endl;
return 0;
}
运行截图:
Markdown已码6.7万字!第4.4章更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
4.5 递增和递减运算符
4.17:说明前置运算符和后置运算符的区别.
答案:
递增和递减运算符有两种形式:前置版本和后置版本.前置版本首先将运算对象加1(或减1),然后把改变后的对象作为求值结果.后置版本也将运算对象加1(或减1),但是求值结果是运算对象改变之前那个值的副本.这两种运算符必须作用于左值运算对象.前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回.
这里的建议是,除非必须,否则不用递增(递减)运算符的后置版本.前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了的运算对象.与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容.如果我们不需要修改之前的值,那么后置版本的操作本来就是一种浪费.
对于整数和指针类型来说,编译器可能对这种额外的工作进行了一定的优化;但是对于相对复杂的迭代器类型来说,这种额外的工作就消耗巨大了.建议养成使用前置版本的习惯,这样不仅不需要担心性能问题,而且更重要的是写出的代码会更符合编程人员的初衷.
4.18:如果第132页那个输出vector对象元素的while循环使用前置递增运算符,将得到什么结果?
答案:
会产生三个错误结果:
一是无法输出vector对象的第一个元素;
二是如果当所有元素不为负时,移动到最后一个元素的地方,程序试图继续向前移动迭代器并解引用一个并不存在的元素;
三是会将-1也进行输出
#include <iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> v = {5,2,0,-1,1,3,1,4};
auto pbeg = v.begin();
while(pbeg != v.end() && *pbeg >= 0)
{
cout<<*++pbeg<<endl;
}
return 0;
}
运行截图:
4.19:假设ptr的类型是指向int的指针、vec的类型是vector、ival的类型是int,说明下面的表达式是何含义?如果有表达式不正确,为什么?应该如何修改?
(a)ptr!=0&&*ptr++
(b)ival++&&ival
©vec[ival++] <= vec[ival]
答案:
(a)ptr!=0&&*ptr++
1.首先判断指针ptr是不是为空,如果不为空的话
2.继续判断当前指针ptr所指的整数是否为非0数;
3.如果非0,则该表达式的最终求值结果为真;否则为假.
4.最后把指针ptr向后移动一位.
该表达式从语法上分析是合法的,但是最后的指针移位操作不一定有意义.如果ptr所指的是整型数组中的某个元素,则ptr可以按照预期移动到下一个元素,如果ptr所指的只是一个独立的整数变量,则移动指针操作将产生未定义的结果
(b)ival++&&ival
1.先检查ival的值是否非0
2.如果非0继续检查(ival+1)的值是否非0.
只有当两个值都是非0值时,表达式的求值结果为真,否则为假.如果二元运算符的两个运算对象涉及同一个对象并改变对象的值,则这是一种不好的程序写法,应该改写所以按照程序的原意,本式应该改写成ival&&(ival+1)
©vec[ival++] <= vec[ival];
含义是比较vec[ival]与vec[ival+1]的大小,如果前者较小则求值结果为真,否则为假,与(b)式一样,本式也出现了二元运算符的两个运算对象涉及同一个对象并改变对象值的情况,应该改写为:vec[ival] <= vec[ival+1]
验证测试代码见下代码块:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int temp[7] = {1,3,5,7,9,11,13};
int *ptr = temp;
int ival = 0;
vector<int> vec = {5,2,0,1,3,1,4};
if(ptr != 0&&*ptr++)
cout<<"if:"<<"*ptr : "<<*ptr<<" ptr : "<<ptr<< endl;
else
{
cout<<"el:"<<"*ptr : "<<*ptr<<" ptr : "<<ptr<<endl;
}
if(ival++&&ival)
cout<<"if:"<<"ival:"<<ival<<endl;
else
cout<<"el:"<<"ival:"<<ival<<endl;
while(ival < 6)
{
if(vec[ival++] <= vec[ival])
cout<<"第"<<ival-1<<"位"<<vec[ival-1]<<"小于等于第"<<ival<<"位"<<vec[ival]<<endl;
else
cout<<"第"<<ival-1<<"位"<<vec[ival-1]<<"大于第"<<ival<<"位"<<vec[ival]<<endl;
}
return 0;
}
运行截图:
Markdown已码6.9万字!第4.5章更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
4.6 成员访问运算符
4.20:假设iter的类型是vector::iterator,说明下面的表达式是否合法.如果合法,表达式的含义是什么?如果不合法,错在何处?
(a)*iter++;(b)(*iter)++;©*iter.empty();
(d)iter->empty();(e)++*iter;(f)iter+±>empty();
答案:
(a)*iter++;//合法的,因为后置递增运算符的优先级要高于解应用运算符,其含义是解引用当前迭代器所处位置的对象内容,然后把迭代器的位置向后移动一位
(b)(*iter)++;//不合法,因为解引用iter获取到vector对象当前的元素,结果是一个string,显然string没有后置递增操作
©*iter.empty();//不合法,因为解引用运算符的优先级低于点运算符,所以该式先计算iter.empty(),而迭代器没有定义empty函数,所以无法通过编译
(d)iter->empty();//合法,iter->empty()等价于(*iter).empty();解引用迭代器得到迭代器当前所指的元素,结果是一个string,显然字符串可以判断是否为空,empty函数在此处有效
(e)++*iter;//不合法,自增运算符的优先级比解引用运算符高,该式先解引用iter,得到迭代器当前所指的元素,结果是一个string,显然string没有后置递增操作
(f)iter++ ->empty();//合法,等价于(*iter++).empty();含义是解应用迭代器当前位置的对象内容,得到一个字符串,判断该字符串是否为空,然后把迭代器向后移动一位
测试验证代码见代码块儿:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<string> svec = {"Dreaming","about","the","things","that","we","could","be."};
auto iter = svec.begin();
cout<<"*iter++:"<<*iter++<<endl;
//cout<<"(*iter)++:"<<(*iter)++<<endl;
//cout<<"*iter.empty():"<<*iter.empty()<<endl;
cout<<"iter->empty():"<<iter->empty()<<endl;
//cout<<"++*iter:"<<++*iter<<endl;
cout<<"iter++->empty():"<<iter++->empty()<<endl;
return 0;
}
运行截图:
Markdown已码7万字!第4.6章更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
4.7 条件运算符
4.21:编写一段程序,使用条件运算符从vector中找到哪些元素的值是奇数,然后将这些奇数值翻倍
答案:见代码块
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec;
srand((unsigned)time(NULL));
for(auto i = 0 ; i != 10;i++ )
ivec.push_back(rand()%1000);
cout<<"生成的随机原数列为:";
for(auto a:ivec)
cout<<a<<" ";
cout<<endl;
for(auto &a:ivec)
(a%2) ? a:a*=2;
cout<<"处理后的随机数列为:";
for(auto a:ivec)
cout<<a<<" ";
cout<<endl;
return 0;
}
运行截图:
4.22: 本节的示例程序将成绩划分成high pass、pass和fail三种,扩展该程序使其进一步将60分到75分之间的成绩设定为low pass.要求程序包含两个版本:一个版本只使用条件运算符;另外一个版本使用1个或多个if语句.哪个版本的程序更容易理解呢?为什么?
答案:见代码块
#include <iostream>
using namespace std;
int main()
{
int grade;
char temp = 'y';
while(1)
{
cout<<"请输入成绩:"<<endl;
cin>>grade;
cout<<"条件运算符版本:"<<endl;
cout<<((grade > 90)?"high pass":((grade > 75)?"pass":((grade>=60)?"low pass":"fail")))<<endl;
cout<<"if语句版本:"<<endl;
if(grade>90)
cout<<"high pass"<<endl;
else if(grade > 75)
cout<<"pass"<<endl;
else if(grade >= 60)
cout<<"low pass"<<endl;
else
cout<<"fail"<<endl;
cout<<"是否继续输入(y or n )?"<<endl;
cin>>temp;
if(temp != 'y' && temp != 'Y')
break;
}
return 0;
}
运行截图:
4.23:因为运算符的优先级问题,下面这条表达式无法通过编译.根据4.12节中的表(第147页)指出它的问题在哪里?应该如何修改?
string a = “word”;
string p1 = a + a[s.size() - 1] == ‘s’ ? “”:“s”;
答案:
涉及到的几个运算符的优先级次序从高到低是加法运算符、相等运算符、条件运算符,因此式子的求职过程是先把s和s[s.size()-1]相加得到一个新字符串,然后该字符串与字符’s’比较是否相等,这是一个非法操作,并且与程序的原意不符要想实现程序的原意,即先判断字符串s的最后一个字符’s’,我们应该添加括号强制限定运算符的执行顺序,修改过程见代码块
#include<iostream>
#include<string>
using namespace std;
int main()
{
string a = "word";
string p1 = a + ((a[a.size() - 1] == 's') ? "":"s");
cout<<p1<<endl;
return 0;
}
运行截图:
4.24:本节的示例程序将成绩划分成high pass、pass和fail三种,它的依据是条件运算符满足右结合律.假如条件运算符满足的是左结合律,求值过程将是怎样的?
答案:
原文的程序是:finalgrade = (grade > 90) ? “high pass”:(grade < 60)?“fail”:“pass”;根据左结合律的含义,该式等价于:finalgrade = ((grade > 90)?“high pass”:(grade < 60))?“fail”:“pass”;先考查grade>90是否成立,如果成立,第一个条件表达式的值为"high pass";如果不成立,第一个条件表达式的值为grade<60.这条语句是无法编译通过的,因为条件运算符要求两个结果表达式的类型相同或者可以互相转化.即使假设语法上通过,也就是说,第一个条件表达式的求值结果分为3种,分别是"high pass"、1和0.接下里根据第一个条件表达式的值求解第二个条件表达式,求值结果是"fail"或"pass".上述求值过程显然与我们的期望是不符的.
Markdown已码7.3万字!第4.7章更新完毕:点击链接进入原作
创作不易!转载请标明: https://blog.csdn.net/qq130106486/article/details/103810949
第5章 语句
5.1 简单语句
5.1:什么是空语句?什么时候会用到空语句?
答案:
空语句是最简单的语句,空语句是一个单独的分号构成.如果在程序的某个地方,语法上需要一条语句但是逻辑上不需要,此时应该使用空语句,空语句什么也不做.
一种常见的情况是,当循环的全部工作在条件部分就可以完成时,我们通常会用到空语句.使用空语句时最好加上注释从而令代码的阅读者知道这条语句是有意省略内容的
第6章 函数
6.1 函数基础
6.1:实参和形参的区别是什么?
答案:
形参出现在函数定义的地方,形参列表可以包含0个、1个或多个形参,多个形参之间以逗号分隔.形参规定了一个函数所能接受数据的类型和数量.
实参出现在函数调用的地方,实参的数量与形参一样多.实参的主要作用是初始化形参,并且这种初始化过程是一一对应的,即第一个实参初始化第一个形参、第二个实参初始化第二个形参,以此类推.实参的类型必须与对应的形参类型匹配.
第7章 类
7.1 定义抽象数据类型
7.1:使用2.6.1节练习定义的Sales_data类为1.6节(第21页)的交易处理程序写一个新版本.
答案:见代码块,测试输入文件下载链接:点击链接开始下载
#include<iostream>
using namespace std;
struct Sale_data{
std::string bookNo;//书籍编号
unsigned int units_sold = 0;//销售量
double sellingprice = 0.0;//零售价
double saleprice = 0.0;//实售价
double discount = 0.0;//折扣
};
int main()
{
Sale_data total;
if(cin>> total.bookNo>>total.units_sold>>total.saleprice)
{
Sale_data trans;
while(cin>> trans.bookNo>>trans.units_sold>>trans.saleprice)
{
if(total.bookNo == trans.bookNo)
{
total.units_sold += trans.units_sold;
}
else
{
cout<<"书籍编号:"<<total.bookNo<<",销售量:"<<total.units_sold<<",总销售额:"<<total.units_sold*total.saleprice<<endl;
total = trans;
}
}
cout<<"书籍编号:"<<total.bookNo<<",销售量:"<<total.units_sold<<",总销售额:"<<total.units_sold*total.saleprice<<endl;
}
else
{
cout<<"No Data!"<<endl;
}
}
运行截图:
第8章 IO库
8.1 IO类
8.1:编写函数,接受一个istream&参数,返回值类型也是istream&.此函数须从给定流中读取数据,直到遇到文件结束标识时停止.它将读取的数据打印在标准输出上.完成这些操作后,在返回流之前,对流进行复位,使其处于有效状态
答案:见代码块儿
#include<iostream>
using namespace std;
istream& readStream(istream &is)
{
int v;
while(is >> v,!is.eof())
{
if(is.bad())
throw runtime_error("IO stream error");
if(is.fail()){
cerr<<"data errror,please input again:"<<endl;
is.clear();
is.ignore(100,'\n');
continue;
}
cout<<v<<endl;
}
is.clear();
return is;
}
int main()
{
cout<<"please input some int,按Ctrl+Z结束"<<endl;
readStream(cin);
return 0;
}
运行截图:
第9章 顺序容器
9.1 顺序容器概述
9.1:对于下面的程序任务,vector、deque和list哪种容器最为适合?解释你的选择的理由.如果没有哪一种容器优于其他容器,也请解释理由.
(a)读取固定数量的单词,将它们按字典顺序插入到容器中.我们将在下一章中看到,关联容器更适合这个问题.
(b)读取未知数量的单词,总是将新单词插入到末尾.删除操作在头部进行
©从一个文件读取未知数量的整数.将这些数排序,然后将它们打印到标准输出.
答案:
(a)"按字典序插入排序到容器中"意味着进行插入排序操作,从而需要在容器内部频繁进行插入操作,vector在尾部之外的位置插入和删除元素很慢,deque在头尾之外的位置插入和删除元素很慢,而list在任何位置插入、删除速度都很快.因此,这个任务选择list更为适合.当然,如果不是必须边读取单词边插入到容器中,可以使用vector,将读入的单词依次追加到尾部,读取完毕后,调用标准库到排序算法将单词重排为字典序.
(b)由于需要在头、尾分别进行插入、删除操作,因此将vector排除在外,deque和list都可以达到很好的性能.如果还需要频繁进行随机访问,则deque更好
(c )由于整数占用空间很小,且快速的排序算法需要频繁随机访问元素,将list排除在外.由于无须在头部进行插入、删除操作,因此使用vector即可,无须使用deque.
第10章 泛型算法
10.1 概述
10.1:头文件algorithm中定义了一个名为count的函数,它类似find,接受一对迭代器和一个值作为参数.count返回给定值在序列中出现的次数.编写程序,读取int序列存入vector中,打印有多少个元素的值等于给定值.
答案:见代码块儿
/*
泛型算法的使用其实很简单,记住关键一点:泛型算法不会直接调用容器的操作,而是通过迭代器来
访问、修改、移动元素.对于本题,将vector的begin和end传递给count,并将要查找的值作为第三个参
数传递给它即可.
*/
#include<algorithm>
#include<vector>
#include<iostream>
#include<fstream>
using namespace std;
int main(int argc,char **argv)
{
ifstream in(argv[1]);
if(!in)
{
cout<<"打开文件失败"<<endl;
return -1;
}
vector<int> ivec;
srand((unsigned)time(NULL));
int temp;
while(in>>temp)
ivec.push_back(temp);
int val;
cout<<"请输入你要查询的数字:(0-9)"<<endl;
cin>>val;
cout<<val<<"出现的个数:"<<count(ivec.begin(),ivec.end(),val)<<endl;
in.close();
return 0;
}
运行截图:
第11章 关联容器
11.1 使用关联容器
11.1:描述map和vector的不同.
答案:
学习关联容器,理解与顺序容器的不同,最关键的是理解其基础的数据结构,随后它所表现出来的一些性质就很自然能够理解了.
两类容器的根本差别在于,顺序容器中的元素是"顺序"存储的(链表容器中的元素虽然不是在内存中"连续"存储的,但仍然是按"顺序"存储的).理解顺序的关键,是理解容器支持的操作形式以及效率.
对于vector这样的顺序容器,元素在其中按顺序存储,每个元素有唯一对应的位置编号,所有操作都是按编号(位置)进行的.例如,获取元素(头、尾、用下标元素获取任意位置)、插入删除元素(头、尾、任意位置)、遍历元素(按元素位置顺序逐一访问).底层的数据结构是数组、链表,简单但已能保证上述操作的高效.而对于依赖值的元素访问,例如查找(搜索)给定值(find),在这种数据结构上的实现是要通过遍历完成的,效率不佳.
而map这种关联容器,就是为了高效实现"按值访问元素"这类操作而设计的.为了达到这一目的,容器中的元素是按关键字值存储的,关键字值与元素数据建立起对应关系,这就是"关联"的含义.底层数据结构是红黑树、哈希表等,可高效实现按关键字查找、添加、删除元素等操作.
第12章 动态内存
12.1 动态内存与智能指针
12.1: 在此代码的结尾,b1和b2各包含多少个元素?
strBlob b1;
{
StrBlob b2 = {"a","an","the"};
b1 = b2;
b2.push_back("about");
}
答案:见代码块儿
/*
由于StrBlob的data成员是一个指向string的vector的shared_ptr,因此StrBlob的赋值不会拷贝vector的内容,
而是多个StrBlob对象共享同一个(创建于动态内存空间上)vector对象.
代码第3行创建b2时提供了3个string的列表,因此会创建一个包含3个string的vector对象,并创建一个shared_
ptr指向此对象(引用计数为1).
第4行将b2赋予b1时,创建一个shared_ptr也指向刚才创建的vector对象,引用计数变为2.因此,第4行向b2添
加 一个string时,会向两个StrBlob共享的vector中添加此string.最终,在代码结尾,b1和b2均包含4个string.
*/
#include"../include/StrBlob.h"
#include<iostream>
using namespace std;
int main()
{
StrBlob b1;
{
StrBlob b2 = {"a","an","the"};
b1 = b2;
b2.push_back("about");
cout<<b1.size()<<endl;
cout<<b2.size()<<endl;
}
return 0;
}