练习2.1 类型 int、long、long long 和 short 的区别是什么?无符号类型和带符号类型的区别是什么?float 和 double的区别是什么?
它们的最小存储空间不同,分别为int(16位),long(32位),long long(64位),short(16位)。
无符号类型只能表示大于等于0的数。带符号类型可以表示正数,负数和0。
它们存储位数不同(float为32位,double为64位),因而取值范围不同,精度也不同(float精度为6位,double精度为10位)。
练习2.2 计算按揭贷款时,对于利率、本金和付款分别应选择何种数据类型?说明你的理由。
利率可以选择float 类型,因为利率通常为百分之几。一般只保留到小数点后两位,所以6 位有效数字就足以表示了。本金可以选择long 类型,因为本金通常为整数。long 类型可表示的最大整数一般为2^31-1(即2147483647),应该足以表示了。付款额一般为实数,可以选择double 类型,因为float 类型的6 位有效数字可能不足以表示。
练习2.3/2.4
#include
using namespace std;
int main()
{
unsigned u = 10, u2 = 42;
cout << u2 - u << endl; //32
cout << u - u2 << endl; //4294967264 -30%4294967296
int i = 10, i2 = 42;
cout << i2 - i << endl; //32
cout << i - i2 << endl; //-32
cout << i - u2 << endl; //4294967264 该行为自测多余
std::cout << i - u << std::endl; //0
cout << u - i << endl; //0
return 0;
}
练习2.5 指出下述字面值的数据类型并说明每一组内几种字面值的区别:
(a) 'a' char类型, L'a' wchar类型, "a" 字符串类型, L"a"宽字符串类型
(b)10 int类型, 10u unsigned int类型, 10L long类型, 10uL unsigned long类型, 012 八进制表示的int类型, 0xC 十六进制表示的int类型
(c)3.14 double类型, 3.14f float类型, 3.14L double类型
(d)10 int类型, 10u unsigned int类型, 10. double类型, 10e-2 double类型
练习2.6 下面两组定义是否有区别,如果有,请叙述之:
int month = 9, day = 7;
int month = 09, day = 07;
有区别,前者定义了month和day变量,值分别为9和7;后者定义了month和day变量,值分别为八进制的9(错误,编译器会报错)和7。
练习2.7 下述字面值表示何种含义?它们各自的数据类型是什么?
(a)"Who goes with Fergus?\n" 类型是字符串类型
(b)31.4 类型是double
(c)1024 类型是float
(d)3.14 类型是double
练习2.8 请利用转义序列编写一段程序,要求先输出 2M,然后转到新一行。修改程序使其先输出 2,然后输出制表符,再输出 M,最后转到新一行。
#include
using namespace std;
int main()
{
cout << "\62\115\12";
cout << "\62\t\115" <
练习2.9 解释下列定义的含义,对于非法的定义,请说明错在何处并将其改正。
(a)(输入时才定义是不合法的)不正确,需在cin之前定义input_value
(b)会强转类型,编译器会报警告warning: narrowing conversion of '3.1400000000000001e+0' from 'double' to 'int' inside { } [-Wnarrowing]|
(c)wage未定义
(d)会强转类型,编译器不会报警告
练习2.10 下列变量的初值分别是什么?
std::string global_str;
int global_int;
int main()
{
int local_int;
std::string local_str;
}
global_str
和 global_int
是全局变量,所以初值分别为空字符串和 0 。 local_int
是局部变量并且没有初始化,它的初值是未定义的。 local_str
是 string 类的对象,它的值由类确定,为空字符串。
注:如果在定义变量时没有指定初值,那么变量会被默认初始化。三条性质:1、定义在任何函数体外的变量会被初始化为0。 2、定义在函数体内部的变量不会被初始化。 3、类的对象未被初始化,则初值由类决定。
练习2.11 指出下面的语句是声明还是定义:
a 声明+定义
b 声明+定义
c 声明
练习2.12 请指出下面的名字中哪些是非法的?
a 非法,与保留字重名
b 合法
c 非法,使用了非法字符
d 非法,以数字开头
e 合法
练习2.13 下面程序中j的值是多少?
int i = 42;
int main()
{
int i = 100;
int j = i;
}
100 局部变量i覆盖了全局i。
练习2.14 下面的程序合法吗?如果合法,它将输出什么?
int i = 100, sum = 0;
for( int i = 0; i != 10; ++i)
sum += i;
std::cout<< i << ' ' << sum << std::endl;
合法,100 45。
练习2.15 下面的哪个定义是不合法的?为什么?
b不合法,引用必须绑定在对象上;d不合法,引用必须初始化
练习2.16 考察下面的所有赋值然后回答:哪些赋值是不合法的?为什么?哪些赋值是合法的?它们执行了哪些操作?
int i = 0, &r1 = i; double d = 0, &r2 = d;
- (a) r2 = 3.14159;
- (b) r2 = r1;
- (c) i = r2;
- (d) r1 = d;
a合法,给d赋值为3.14159;b合法,给d赋值为i(会自动转换 int->double);c合法,给i赋值为d(会发生double->int,小数点后数据丢失);d合法,同c
练习2.17 执行下面的代码段将输出什么结果?
int i, &ri = i;
i = 5; ri = 10;
std::cout << i << " " << ri << std::endl;
10 10
练习2.18 编写代码分别改变指针的值以及指针所指对象的值。
int ival = 1024;
int *p = &ival;
int *p1 = nullptr;
p1 = p;
练习2.19 说明指针和引用的主要区别
1.引用是另一个对象的别名,而指针本身就是一个对象。
2.引用在定义时必须初始化,并且在其生命周期内只能绑定一个对象;指针不必在定义时初始化,也可以重新赋值使其指向其他对象。
练习2.20 请叙述下面这段代码的作用。
int i = 42;
int *p1 = &i;
*p1 = *p1 * *p1;
指针p1指向对象i,然后通过指针p1给对象i重新赋值为42*42(1764)
练习2.21 请解释下述定义。在这些定义中有非法的吗?如果有,为什么?
int i = 0;
(a) double* dp = &i;
(b) int *ip = i;
(c) int *p = &i;
a非法,double型指针指向了int型对象;b非法,不能将int型变量赋给指针;c合法
练习2.22 假设 p 是一个 int 型指针,请说明下述代码的含义。
if (p) // ...
if (*p) // ...
1.判断指针p是否指向nullptr(空指针),是为false,否为true。2.判断指针p指向的变量的值是否为0
练习2.23 给定指针 p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,也请说明原因。
不能,判断指针是否指向了一个合法的对象首先需要知道指针的类型,还要知道指针指向的地址所存储的对象类型,并且还要知道指针指向的地址所存储的对象是否是所期望的对象。这几步在程序中极难做到,所以指针安全需要程序员正确使用指针。
练习2.24 在下面这段代码中为什么 p 合法而 lp 非法?
int i = 42;
void *p = &i;
long *lp = &i;
指针p的类型为void* 他可以用于存放任何对象的地址。指针lp的类型为long,与变量i的类型int不符。
练习2.25 说明下列变量的类型和值。
a,ip为int型指针,i为int型对象,r为int型引用并与对象i绑定。b,i为int型对象,ip为int型空指针。c,ip为int型指针,ip2为int型对象。
练习2.26 下面哪些语句是合法的?如果不合法,请说明为什么?
const int buf; // 不合法, const 对象必须初始化
int cnt = 0; // 合法
const int sz = cnt; // 合法
++cnt; ++sz; // 不合法, const 对象不能被改变
练习2.27 下面的哪些初始化是合法的?请说明原因。
int i = -1, &r = 0; // 不合法, r 必须引用一个对象
int *const p2 = &i2; // 合法
const int i = -1, &r = 0; // 合法
const int *const p3 = &i2; // 合法
const int *p1 = &i2; // 合法
const int &const r2; // 不合法, r2未绑定一个常量对象或者常量
const int i2 = i, &r = i; // 合法
练习2.28:说明下面的这些定义是什么意思,挑出其中不合法的。
int i, *const cp; // 不合法, const 指针必须初始化
int *p1, *const p2; // 不合法, const 指针必须初始化
const int ic, &r = ic; // 不合法, const int 必须初始化
const int *const p3; // 不合法, const 指针必须初始化
const int *p; // 合法. 一个指针,指向 const int
练习2.29 假设已有上一个练习中定义的那些变量,则下面的哪些语句是合法的?请说明原因。
i = ic; // 合法, 常量赋值给普通变量
p1 = p3; // 不合法, p3 是const int型指针不能赋值给int型指针
p1 = ⁣ // 不合法, int型指针不能指向const int型对象
p3 = ⁣ // 不合法, p3是一个常量指针,不能被再次赋值
p2 = p1; // 不合法, p2是一个常量指针,不能被再次赋值
ic = *p3; // 不合法, ic是个常量,不能被再次赋值
练习2.30 对于下面的这些语句,请说明对象被声明成了顶层const还是底层const?
const int v2 = 0; int v1 = v2;
int *p1 = &v1, &r1 = v1;
const int *p2 = &v2, *const p3 = &i, &r2 = v2;
v2顶层const,p2底层const,p3顶层const,底层const
练习2.31 假设已有上一个练习中所做的那些声明,则下面的哪些语句是合法的?请说明顶层const和底层const在每个例子中有何体现。
r1 = v2; // 合法, 顶层const在拷贝时不受影响
p1 = p2; // 不合法, p2 是底层const,如果要拷贝必须要求 p1 也是底层const
p2 = p1; // 合法, int* 可以转换成const int*
p1 = p3; // 不合法, p3 是一个底层const,p1 不是
p2 = p3; // 合法, p2 和 p3 都是底层const,拷贝时忽略掉顶层const
练习2.32 下面的代码是否合法?如果非法,请设法将其修改正确。
int null = 0, *p = null;
非法,(error C2440: “初始化”: 无法从“int”转换为“int *”)可改为
int null = 0, *p = nullptr;
练习2.33 利用本节定义的变量,判断下列语句的运行结果。
a=42; // a 是 int
b=42; // b 是一个 int,(ci的顶层const在拷贝时被忽略掉了)
c=42; // c 也是一个int
d=42; // d 是一个 int *,所以语句非法
e=42; // e 是一个 const int *, 所以语句非法
g=42; // g 是一个 const int 的引用,引用都是底层const,所以不能被赋值
练习2.34 基于上一个练习中的变量和语句编写一段程序,输出赋值前后变量的内容,你刚才的推断正确吗?如果不对,请反复研读本节的示例直到你明白错在何处为止。
#include
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
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
std::cout << d << std::endl;
std::cout << e << std::endl;
std::cout << f << std::endl;
std::cout << g << std::endl;
std::cout << "--------------" << std::endl;
a = 42; b = 42; c = 42; //d = 42; e = 42; g = 42;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
std::cout << d << std::endl;
std::cout << e << std::endl;
std::cout << f << std::endl;
std::cout << g << std::endl;
return 0;
}
练习2.35 判断下列定义推断出的类型是什么,然后编写程序进行验证。
const int i = 42;
auto j = i; const auto &k = i; auto *p = &i;
const auto j2 = i, &k2 = i;
j的类型是int,k为const int型引用,p为const int型指针,j2为const int型,k2为const int型引用。
练习2.36 关于下面的代码,请指出每一个变量的类型以及程序结束时它们各自的值。
int a = 3, b = 4;
decltype(a) c = a;
decltype((b)) d = a;
++c;
++d;
c为int型,d为int &。都为4。
练习2.37 赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int,则表达式 i=x 的类型是 int&。根据这一特点,请指出下面的代码中每一个变量的类型和值。
int a = 3, b = 4;
decltype(a) c = a;
decltype(a = b) d = a;
c为int型,值为3;d为int &型,绑定到对象a上。
练习2.38 说明由decltype 指定类型和由auto指定类型有何区别。请举一个例子,decltype指定的类型与auto指定的类型一样;再举一个例子,decltype指定的类型与auto指定的类型不一样。
主要的区别有两点:
1:如果使用引用类型,auto会识别为其所指对象的类型,decltype则会识别为引用的类型。
2:decltype(())双括号的差别。
decltype 处理顶层const和引用的方式与 auto不同,decltype会将顶层const和引用保留起来。
int i = 0, &r = i;
// same
auto a = i;
decltype(i) b = i;
// different
auto c = r;
decltype(r) d = i;
练习2.39 编译下面的程序观察其运行结果,注意,如果忘记写类定义体后面的分号会发生什么情况?记录下相关的信息,以后可能会有用。
[Error] expected ';' after struct definition
练习2.40 根据自己的理解写出 Sales_data 类,最好与书中的例子有所区别。
struct Sales_data{
std::string bookNo;
std::string bookName;
int num;
double price = 0.0;
};
练习2.41
练习2.42