c++ Primer 第二章:变量和基本练习 练习答案记录

c++ Primer 第二章:变量和基本练习 练习答案记录

练习题导航

下面的练习别忘记都加上下面这一语句

#include<iostream>

2.1 基本内置类型

2.1.1 算术类型

2.1.2 类型转换

练习2.3 2.4 检查自己估计是否正确

int main()
{
	unsigned u = 10, u2 = 42;
	std::cout << u2 - u << std::endl;
	std::cout << u - u2 << std::endl;
	int i = 10, i2 = 42;
	std::cout << i2 - i << std::endl;
	std::cout << i - i2 << std::endl;
	std::cout << i - u << std::endl;
	std::cout << u - i << std::endl;
}

在这里插入图片描述

2.1.3 字面值常量

练习2.8 先输出2,然后输入制表符,再输出M,最后转到新的一行

int main()
{
	std::cout << 2 << "\x4d" << "\n" <<2<< "\t" << "\x4d" << "\n"  <<std::endl;
}

在这里插入图片描述

2.2 变量

2.2.1 变量定义

练习2.9 解释下列定义的含义 对于非法的定义,请说明错在何处并将其改正

int main()
{
	//std::cin >> int input_value;     //错误:意外的类型“int”
	//int i = { 3.14 };                  //错误:error C2397: 从“double”转换到“int”需要收缩转换
	//double salary = wage = 9999.99;     //错误:“wage”: 未声明的标识符
	//int i = 3.14                      //合法,i=3
}

练习2.10 下列变量的处置分别是什么

std::string global_str;         //定义于函数体外初始化为0
int global_int;
int main()
{
	int local_int;               //显示是随机值,未被初始化,若访问将引发错误
	std::string local_str;
}

2.2.2 变量声明和定义的关系

练习2.11 指出下面的语句是声明还是定义

extern int ix = 1024;      //定义
int iy;                    //定义
extern int iz;             //声明
int main()
{
	iy = 10;
	iz = 10;
	std::cout << ix<<iy<<iz<< std::endl;         //错误:无法解析的外部符号 "int iz"
}

2.2.3 标识符

练习2.12 请指出下面的名字中哪些是非法的

int main()
{
	//int double = 3.14;      //错误:“int”后面的“double”非法    “int”: 在“=”前没有声明变量
	//int _;                    //合法
	//int catch - 22;           //错误:没有与该 catch 处理程序关联的 Try 块
	//int 1_or_2 = 1;            //错误:语法错误:“user-defined literal”
	//double Double = 3.14;        //合法
}

2.2.4 名字的作用域

练习2.13 下面程序中j的值是多少

int i = 42;
int main()
{
	int i = 100;
	int j = i;           //j等于100
	std::cout << j << std::endl;
}

在这里插入图片描述

练习2.14 下面的程序合法吗,如果合法它将输出什么

int main()
{
	int i = 100, sum = 0;
	for (int i = 0; i != 10; i++) {
		sum += i;
	}
	std::cout << sum << std::endl;              //合法:输出sum为45
}

在这里插入图片描述

2.3 复合类型

2.3.1引用

练习2.15 下面的哪个定义是不合法的?为什么?

int main()
{
	int ival = 1.01;          //合法
	//int &rval1 = 1.01;          //错误:“初始化”: 无法从“double”转换为“int &”  并且:引用类型的初始值必须是一个对象
	int& rval2 = ival;         //合法
	std::cout << rval2 << std::endl;
	int &rval3;               //错误:“rval3”: 必须初始化引用
}

练习2.16 考察下面的所以赋值然后回答,哪些赋值是不合法的?为什么?哪些赋值是合法的?它们执行了什么样的操作?

int main()
{
	int i = 0, & r1 = i;
	double d = 0, & r2 = d;

	r2 = 3.14159;          //合法,且r2和d都是3.14159
	std::cout << r2 << "and" << d <<"\n" << std::endl;
	r2 = r1;               //合法,且r2和d都是0
	std::cout << r2 << "and" << d << "\n" << std::endl;
	i = r2;                //合法,且i和r1都是0
	std::cout << i << "and" << r1 << "\n" << std::endl;
	r1 = d;                //合法,且i和r1都是0
	std::cout << i << "and" << r1 << "\n" << std::endl;
}

在这里插入图片描述

练习2.17 执行下面的代码段将输出什么结果?

int main()
{
	int i, & ri = i;
	i = 5;
	ri = 10;             //最终i和ri一样都是10,引用我们可以当作i和ri是一个东西,指向同一个地址
	std::cout << i << " " << ri << std::endl;
}

在这里插入图片描述

2.3.2 指针

练习2.18 编写代码分别更改指针的值以及指针所指对象的值

int main()
{
	int i = 10;
	int* p = &i;
	*p = 100;
	std::cout << i << std::endl;
}

在这里插入图片描述

练习2.20 请叙述下面这段代码的作用

int main()
{
	int i = 42;
	int* p1 = &i;
	*p1 = *p1 * *p1;       //相当于i*i
	std::cout << i << std::endl;
}

在这里插入图片描述

练习2.21 请解释下述定义。在这些定义中有非法的吗?如果有,为什么?

int main()
{
	int i = 0;
	//double* dp = &i;                   //错误:“初始化”: 无法从“int *”转换为“double *”   指向的类型不相关; 转换需要 reinterpret_cast、C 样式强制转换或带圆括号的函数样式强制转换
	//int* ip = i;                        //错误:“初始化”: 无法从“int”转换为“int *”  从整型类型转换为指针类型需要 reinterpret_cast、C 样式转换或带圆括号的函数样式强制转换
	int* p = &i;                         //合法
}

练习2.22 假设p是一个int型指针,请说明下述代码的含义

int main()
{
	int i = 10, * p = &i;
	if (p) {
		std::cout << "不带指针" << std::endl;         //这里的p是地址,只要所指的地址不为0就是true
	}
	if (*p) {
		std::cout << "带指针" << std::endl;           //这里的*p是i,只要i不为0就是true
	}
}

练习2.23 给定指针p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路,如果不能,也请说明原因

不能,不能判断指针是否有效

练习2.24 在下面这段代码中为什么p合法而lp非法?

int main()
{
	int i = 42;
	void* p = &i;
	long* lp = &i;           //错误:“初始化”: 无法从“int *”转换为“long *”
}

练习2.25 说明下列变量的类型和值

int main()
{
	int* ip.i,&r=i;      //*ip是int型指针,i是int型,r是对i的引用
	int i, * ip = 0;     //i是int型,*ip的地址是0,指向的是地址为0处的数
	int* ip, ip2;        //*ip是int型指针,ip2是int型
}

2.3.3 理解复合类型的声明

2.4 const限定符

练习2.26 下面哪些句子是合法的?如果有不合法的句子,请说明为什么?

int main()
{
	//const int buf;               //错误:“buf”: 如果不是外部的,则必须初始化常量对象
	int cnt = 0;                   //合法
	const int sz = cnt;            //合法
	++cnt;                         //合法
	++sz;                          //错误:“sz”: 不能给常量赋值
}

2.4.1 const的引用

2.4.2 指针和const

练习2.27 下面的哪些初始化是合法的?请说明原因

int main()
{
	//int i2 = 0;
	//int i = -1, & r = 0;          //错误:“初始化”: 无法从“int”转换为“int &”
	//int* const p2 = &i2;            //合法:但是*p2指针的地址将一直是i2所在地址
	//const int i = -1, & r = 0;      //合法:r是对一个常量的引用
	//const int* const p3 = &i2;      //合法:不能通过指针*p3去改变i2的值,并且*p3指针的地址将一直是i2所在地址
	//const int* p1 = &i2;            //合法:不能通过指针*p1去改变i2的值
	//const int& const r2;          //错误:“r2”: 必须初始化引用
	//int i = -1;
	//const int i2 = i, & r = i;      //合法:i2一直等于i,r一直引用i
}

练习2.28 说明下面的这些定义是什么意思,挑出其中不合法的

int main()
{
	//int i, * const cp;          //错误:“cp”: 如果不是外部的,则必须初始化常量对象
	//int* p1, * const p2;        //错误:“p2”: 如果不是外部的,则必须初始化常量对象
	//const int ic, & r = ic;     //错误:“ic”: 如果不是外部的,则必须初始化常量对象
	//const int* const p3;        //错误:“p3”: 如果不是外部的,则必须初始化常量对象
	const int* p;                 //没有指向,默认分配
}

2.4.3 顶层const

练习2.30 对下面语句,请说明对象被声明成了顶层const还是底层const?

int main()
{
	int i = 1;
	const int v2 = 0;      //v2不能改变,是顶层const
	int v1 = v2;           //不是const
	int* p1 = &v1, & r1 = v1;   //p1,r1都不是
	const int* p2 = &v2, * const p3 = &i, & r2 = v2;   //p2所指对象不能改变,是底层const   p3既是顶层又是底层,r2是底层const
}

练习2.31 假设已有上一个练习所作的那些声明,则下面的哪些语句是合法的?请说明顶层const和底层const在每个例子中有何体现

int main()
{
	int i = 1;
	const int v2 = 0;      //v2不能改变,是顶层const
	int v1 = v2;           //不是const
	int* p1 = &v1, & r1 = v1;   //p1,r1都不是
	const int* p2 = &v2, * const p3 = &i, & r2 = v2;   //p2所指对象不能改变,是底层const   p3既是顶层又是底层,r2是底层const

	r1 = v2;              //r1不是const,可以对r1进行赋值
	//p1 = p2;            //错误,p2是底层const,无法从“const int *”转换为“int *”
	p2 = p1;              //合法
	//p1 = p3;            //错误,p3是也是底层const,无法从“const int *”转换为“int *”
	p2 = p3;              //合法,同是底层const,可以拷贝
}

2.4.4 constexpr和常量表达式

练习2.32 下面的代码是否合法?如果非法,请设法将其修改正确

int main()
{
	//int null = 0, * p = null;         //错误:“初始化”: 无法从“int”转换为“int *”
	//因改为在null前加&
	int null = 0, * p = &null;
}

2.5 处理类型

2.5.1 类型别名

2.5.2 auto类型说明符

练习2.33 2.34 利用本节定义的变量,判断下列语句的运行结果

int main()
{
	int i = 0, & r = i;
	auto a = r;      //a是一个整数(r是i的别名,而i是一个整数)
	const int ci = i, & cr = ci;
	auto b = ci;     //b是一个整数(ci的顶层const特性被忽略掉了)
	auto c = cr;     //c是一个整数(cr是ci的别名,ci本身是一个顶层const)
	auto d = &i;     //d是一个整形指针(整数的地址就是指向整数的指针)
	auto e = &ci;    //e是一个指向整数常量的指针(对常量对象取地址是一种底层const)
	const auto f = ci; //ci的推演类型是int,f是const int
	auto& g = ci;    //g是一个整形常量引用,绑定到ci

	a = 42;      //仅a等于42,i,r还是0
	std::cout << a << " " << r << " " << i << std::endl;
	
	b = 42;      //仅b为42,且不是常量
	
	c = 42;      //仅c为42,且不是常量

	//d = 42;      //“=”: 无法从“int”转换为“int *”

	//e = 42;      //“=”: 无法从“int”转换为“const int *”

	g = 42;        //“g”: 不能给常量赋值
}

练习2.35 判断下列定义推断出的类型是什么,然后编写程序进行验证

int main()
{
	const int i = 42;   //i是顶层const

	auto j = i;         //j是一个整型
	j = 40;

	const auto& k = i;  //忽略i的顶层const,k可以看作对i的一个引用,且k是一个常量
	//k = 40;           //错误:“k”: 不能给常量赋值
	std::cout << k << " " << i << std::endl;   //打印出来,k和i都是42

	auto* p = &i;       //定义了一个指针p,指向一个常量i,这是一个底层const指针p
	std::cout << *p << " " << i << std::endl;
	//*p = 10;          //错误:“p”: 不能给常量赋值
	p = &j;             //指针p可以指向其他,可以说明p仅仅只是一个底层const指针
	std::cout << *p << " " << j << std::endl;

	const auto j2 = i, & k2 = i;    //j2为int型常量,值是42   k2是一个常量,是i的另一种表达方式
	std::cout << j2 << " " << i << std::endl;
	std::cout << k2 << " " << i << std::endl;
}

在这里插入图片描述

2.5.3 decltype类型指示符

练习2.36 关于下面代码,请指出每一个变量的类型以及程序结束时它们各自的值

int main()
{
	int a = 3, b = 4;
	decltype(a) c = a;    //c是int型,且c=a等于3
	std::cout << c << " " << a << std::endl;

	decltype((b)) d = a;  //加两个括号是引用,表示int &d = a,改变d就是改变a,改变a就是改变d
	d = 10;
	std::cout << d << " " << a << std::endl;    //都输出10
	a = 7;
	std::cout << d << " " << a << std::endl;    //都输出7

	++c;
	++d;
	std::cout << d << " " << a << std::endl;    //d,a都输出8
	std::cout << c << " " << a << std::endl;    //c输出4
}

在这里插入图片描述

练习2.37 赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说

如果i是int,则表达式i=x的类型是int&。根据这一特点,请指出下面的代码中每一个变量的类型和值

int main()
{
	int a = 3, b = 4;         
	decltype(a) c = a;       //c是int型,且等于3
	decltype(a = b) d = a;   //d是int型,且等于3
	decltype(a == b) e = a;   //a由于不等于b,这里输出一个bool型,由于a不等于0,所以d是1
	std::cout << c << " " << e << std::endl;
}

在这里插入图片描述

练习2.38 说明由decltype指定类型和由auto指定类型有什么区别。

请举出一个例子,decltype指定类型和由auto指定类型一样 再举一个例子,decltype指定的类型与auto指定的类型不一样

int main()
{
	int i = 10;
	decltype(i) a = i;
	auto b = i;
	std::cout << a << " " << b << std::endl;   //指定的类型一样

	const int j = 10;
	auto c = j;
	decltype(j) d = j;
	c = 100;    //正确:c不是常量
	d = 100;    //错误:“d”: 不能给常量赋值
	//指定的类型不一样
}

2.6 自定义数据结构

2.6.1 定义Sales_data类型

练习2.39 编译下面的程序观察其运行结果,注意,如果忘记写类定义体后面的分号会发生说明情况? 记录下相关信息,以后可能会有用

struct Foo {}      //错误:“Foo”后面接“int”是非法的(是否忘记了“;”?)    “main”的返回类型应为“int”而非“Foo”   
//“return”: 无法从“int”转换为“Foo”      无构造函数可以接受源类型,或构造函数重载决策不明确
int main()
{
	return 0;
}

练习2.40 根据自己的理解写出Sales_data类,最好于书中的例子有所区别

struct Sales_data {
	std::string book_name;    //图书编号
	unsigned book_sales = 0;  //图书售出数量
	double unit_price = 0;    //图书销售单价
};

2.6.2 使用Sales_data类

练习2.41(1) 使用你自己的Sales_data重写1.5.1节(第20页)的练习

眼下先把Sales_data类的定义和main函数放在同一个文件里

struct Sales_data {
	std::string book_name;    //图书编号
	unsigned book_sales = 0;  //图书售出数量
	double unit_price = 0;    //图书销售单价
	double book_revenue = 0;  //计算销售收入
};
int main()
{
	//1.5.1节练习
	Sales_data data,data1,data2;
	int i = 0;
	std::cout << "请输入所需要进入的练习程序中:" << std::endl;
	std::cin >> i;
	switch (i) {
	case 1:
		std::cout << "练习1.20 编写一个程序,读取一组书籍销售记录,将每条记录打印到标准输出上\n" << std::endl;
		while (std::cin >> data.book_name) {
			if (data.book_name == "over")          //当输入over的时候,跳出循环
				break;
			std::cin >> data.book_sales >> data.unit_price;
			data.book_revenue = data.book_sales * data.unit_price;
			std::cout << data.book_name << " " << data.book_revenue << " " << data.book_sales << " " << data.unit_price << std::endl;
		}
		break;
	case 2:
		std::cout << "练习1.21 编写程序,读取两个ISBN相同的Sales_data对象,输出它们的和\n" << std::endl;
		while (std::cin >> data1.book_name) {
			if (data1.book_name == "over")          //当输入over的时候,跳出循环
				break;
			std::cin >> data1.book_sales >> data1.unit_price;
			std::cin >> data2.book_name >> data2.book_sales >> data2.unit_price;   //输入第二本书
			if (data1.book_name == data2.book_name) {
				data.book_name = data1.book_name;
				data.book_sales = data1.book_sales + data2.book_sales;
				data.unit_price = data1.unit_price + data2.unit_price;
				data.book_revenue = data.book_sales * data.unit_price;
				std::cout << data.book_name << " " << data.book_revenue << " " << data.book_sales << " " << data.unit_price << std::endl;
			}
			else {
				std::cout << "所输入两本书籍书名不相同" << std::endl;
				break;
			}
		}
		break;
	case 3:
		std::cout << "练习1.22 编写程序,读取多个具有相同isbn的销售记录,输出所有记录的和\n" << std::endl;
		if (std::cin >> data1.book_name) {     //输入第一本书
			if (data1.book_name == "over")          //当输入over的时候,跳出循环
				break;
			std::cin >> data1.book_sales >> data1.unit_price;
			while (std::cin >> data2.book_name) {
				if (data2.book_name == "over")          //当输入over的时候,跳出循环
					break;
				std::cin >> data2.book_sales >> data2.unit_price;
				if (data1.book_name == data2.book_name) {                          //如果两书名字相同
					data1.book_sales = data1.book_sales + data2.book_sales;
					data1.unit_price = data1.unit_price + data2.unit_price;
				}
				else {
					data1.book_revenue = data1.book_sales * data1.unit_price;
					std::cout << data1.book_name << " " << data1.book_revenue << " " << data1.book_sales << " " << data1.unit_price << std::endl;
					data1.book_name = data2.book_name;
					data1.book_sales = data2.book_sales;
					data1.unit_price = data2.unit_price;
				}
			}
		}
		break;
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

练习2.41(2) 使用你自己的Sales_data重写1.5.2节(第21页)的练习

眼下先把Sales_data类的定义和main函数放在同一个文件里

struct Sales_data {
	std::string book_name;    //图书编号
	unsigned book_sales = 0;  //图书售出数量
	double unit_price = 0;    //图书销售单价
	double book_revenue = 0;  //计算销售收入
};
int main()
{
	Sales_data data, data1, data2;
	int i = 0, sum = 1;
	std::cout << "请输入所需要进入的练习程序中:" << std::endl;
	std::cin >> i;
	switch (i) {
	case 1:
		std::cout << "练习1.23 编写程序,读取多条销售记录,并统计每个ISBN(每本书)有几条销售记录" << std::endl;
		if (std::cin >> data1.book_name) {
			if (data1.book_name == "over")
				break;
			std::cin >> data1.book_sales >> data1.unit_price;
		}
		while (std::cin >> data2.book_name) {
			if (data2.book_name == "over")          //当输入over的时候,跳出循环
				break;
			std::cin >> data2.book_sales >> data2.unit_price;
			if (data1.book_name == data2.book_name) {                          //如果两书名字相同
				sum++;
			}
			else {
				std::cout << data1.book_name << "卖出了:" << sum << "本" << std::endl;
				data1.book_name = data2.book_name;
				data1.book_sales = data2.book_sales;
				data1.unit_price = data2.unit_price;
				sum = 1;
			}
		}
		break;
	case 2:
		std::cout << "练习1.24 输入表示多个ISBN的多条销售记录来测试上一个程序,每个ISBN的记录应该聚在一起" << std::endl;
		break;
	}
}

在这里插入图片描述

2.6.3 编写自己的头文件

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Is_LiuYiZheng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值