Chapter 2 练习题
环境: CLion + MinGW
2.1 类型int、long、long long和short的区别是什么?无符号类型和带符号类型的区别是什么?float和double的区别是什么?
(1) int、long、long long和short都是整型,在C++中规定,int至少和short一样大,long至少和int一样大,long long至少和long一样大
在Win64电脑上,环境为CLion+MinGW64+GCC8.1.0运行以下程序
cout<<"Size of short is "<<sizeof(short)<<" bytes."<<endl;
cout<<"Size of int is "<<sizeof(int)<<" bytes."<<endl;
cout<<"Size of long is "<<sizeof(long)<<" bytes."<<endl;
cout<<"Size of long long is "<<sizeof(long long)<<" bytes."<<endl;
结果为
Size of short is 2 bytes.
Size of int is 4 bytes.
Size of long is 4 bytes.
Size of long long is 8 bytes.
(2) 无符号类型只能表示非负数,没有符号位;有符号类型可以表示正数、负数和零,最高位为符号位
(3) float为单精度浮点数,double为双精度浮点数
在Win64电脑上,环境为CLion+MinGW64+GCC8.1.0运行以下程序
cout<<"Size of float is "<<sizeof(float)<<" bytes."<<endl;
cout<<"Size of double is "<<sizeof(double)<<" bytes."<<endl;
cout<<"Size of long double is "<<sizeof(long double)<<" bytes."<<endl;
结果为
Size of float is 4 bytes.
Size of double is 8 bytes.
Size of long double is 16 bytes.
2.2 计算按揭贷款时,对于利率、本金和付款分别应选择何种数据类型?说明你的理由
一般情况下均选择double,double精度比float高且计算代价相当,而long double精度更高但计算代价较大
2.3 读程序写结果
unsigned u = 10, u2 = 42;
std::cout << u2 - u << std:endl; // 32
std::cout << u - u2 << std:endl; // 2^32 - 32
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; // 0
std::cout << u - i << std::endl; // 0
// 补充
std::cout << u - i2 << std::endl; // 2^32 - 32, 混用带符号数和无符号数, 带符号数转换为无符号数
2.4 同上
2.5 指出下述字面值的数据类型并说明每一组内几种字面值的区别
'a', L'a', "a", L"a" // 字符a;宽字符型字面值a,类型为wchar_t;字符串a,宽字符型字符串a
10, 10u, 10L, 10uL, 012, 0xC // int;unsigned int; long; unsigned long; 八进制数(10);十六进制数(12)
3.14, 3.14f, 3.14L // double; float; long
10, 10u, 10., 10e-2 // int; unsigned; double; double(0.1)
2.6 下面两组定义是否有区别
int month = 9, day = 7;
int month = 09, day = 07; // 八进制数, 且09会引起编译器报错, 应为011
2.7 下述字面值表示何种含义?它们各自的数据类型是什么?
"Who goes with F\145rgus?\012" // 转义字符,\145表示字符e,\012为换行符
3.14e1L // long double型(31.4)
1024f // float型(1024.0)
3.14L // long double型(3.14)
2.8 用转义字符输出2M并换行,然后修改程序让其输出2,再输出制表符,再输出M并换行
int main(){
std::cout << "2\x4d\012";
std::cout << "2\t\x4d\012";
return 0;
}
2.9 解释下列定义的含义,并改正非法定义
std::cin >> int input_value; // input_value应提前定义
int i = {3.14}; // i被赋值为3
double salary = wage = 9999.99; // wage未提前定义, 报错
int i = 3.14; // i被赋值为3
对于第三行,若wage提前定义,则正确
double wage;
double salary = wage = 9999.99; // 该语句定义变量salary,给wage赋值,并对salary初始化
2.10 下列变量的初值分别是什么?
std::string global_str; // string类默认初始化为空字符串
int global_int; // 函数体外定义int型, 默认初始化为0
int main(){
int local_int; // 函数体内定义基础类型, 未初始化则值为未定义
std::string local_str; // string类默认初始化为空字符串
}
2.11 指出下面的语句是声明还是定义
extern int ix = 1024; // 定义, 给extern标记的变量赋值将抵消extern的作用, 变为定义语句
int iy; // 定义和声明
extern int iz; // 声明
2.12 指出下面的名字哪个是非法的?
int double = 3.14; // 非法, double为C++保留的关键字
int _; // 在函数体内时非法, 函数体内名字不能以_开头
int catch-22; // 非法, -不是有效名字
int 1_or_2 = 1; // 非法, 不能以数字开头
double Double = 3.14; // 合法但不好, 变量名一般均为小写字母, 自定义类名以大写字母开头
2.13 下面程序中j的值是多少?
int i = 42;
int main(){
int i = 100; // 内层作用域的i覆盖了外层作用域的i
int j = i; // j的值为100
}
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 下面的定义哪个是非法的?为什么?
int ival = 1.01;
int &rval1 = 1.01; // 非法, 引用不能绑定字面值常量
int &rval2 = ival;
int &rval3; // 非法, 引用必须初始化
2.16 哪些赋值是非法的?为什么?
int i = 0, &r1 = i;
double d = 0, &r2 = d;
r2 = 3.14159; // 将3.14159赋给d
r2 = r1; // 将0.0赋给d
i = r2; // 将0赋给i
r1 = d; // 将0赋给i
// 均为合法的
2.17 执行下面的代码段将输出什么结果?
int i, &ri = i;
i = 5;
ri = 10;
std::cout << i << " " << ri << std::endl; // 10 10
2.18 编写代码分别更改指针的值及指针所指对象的值
int a = 1, *pa = &a;
*pa = 2; // 改变pa所指对象a的值为2
pa = nullptr; // 改变pa的值为空指针
2.19 说明指针和引用的主要区别
(1) 引用必须初始化,且此后将始终绑定该对象;指针可以不初始化,其值能够改变
(2) 引用本身不是对象,指针本身就是对象
2.20 叙述下面代码的作用
int i = 42;
int *pi = &i; // 指针pi指向变量i
*pi = *pi * *pi; // 取变量i与变量i相乘, 结果存入变量i, 故i的终值为42*42
2.21 解释下属定义,判断是否非法
int i = 0;
double *dp = &i; // 非法, dp为指向double对象的指针, 但初始化指向了int变量
int *ip = i; // 非法, ip为指针, 不能直接用int变量赋值, 只能用int变量的地址赋值
int *p = &i; // 合法
2.22 假设p是一个int型指针,说明下述代码的含义
if(p){} // 若p不是空指针, 执行块中语句
if(*p){} // 若p指向对象不为0, 执行块中语句
2.23 给定指针p,你能知道它是否指向了一个合法的对象吗?
把p作为if语句条件,若为空指针则条件为假,反之为真
2.24 为什么p合法而lp非法
int i = 42;
void *p = &i; // void*指针可以指向任意类型的对象
long *lp = &i; // long指针不能指向int变量
2.25 说明下列变量的类型和值
int *ip, i, &r = i; // ip为int指针, i为int变量, r为绑定i的引用
int i, *ip = 0; // i为int变量, ip为int指针,当前值为空
int *ip, ip2; // ip为int指针, ip2为int变量
2.26 判断是否合法
const int buf; // 非法,const对象必须初始化
int cnt = 0;
const int sz = cnt; // 合法,const int对象可用int变量初始化
++cnt;
++sz; // 非法,const int变量的值不可改变
2.27 判断初始化是否合法
int i = -1, &r = 0; // 非法,引用不能绑定字面值常量
int *const p2 = &i2; // 合法,p2是一个常量指针(顶层const)
const int i = -1, &r = 0; // 合法,常量引用可以绑定字面值常量
const int *const p3 = &i2; // 合法,p3是一个指向常量对象的常量指针
const int *p1 = &i2; // 合法
const int &const r2; // 非法,引用必须初始化,且引用不是对象,不能指定顶层const
const int i2 = i, &r = i; // 合法
2.28 说明下面的定义是什么意思,挑出其中不合法的
int i, *const cp; // 非法,常量指针必须初始化
int *p1, *const p2; // 非法,常量指针必须初始化
const int ic, &r = ic; // 非法,ic必须初始化
const int *const p3; // 非法,常量指针必须初始化
const int *p; // 合法,指针p指向int常量
2.29 假设已有2.28定义的变量,下面的语句哪些是合法的
i = ic; // 合法
p1 = p3; // 非法,常量指针不可指向常量对象
p1 = ⁣ // 非法,普通指针不能指向常量对象
p3 = ⁣ // 非法,常量指针的值不可变
p2 = p1; // 非法,常量指针的值不可变
ic = *p3; // 非法,const int的值不可变
2.30 判断对象被声明为顶层const还是底层const
const int v2 = 0; // 顶层const
int v1 = v2;
int *p1 = &v1, &r1 = v1;
const int *p2 = &v2, *const p3 = &i, &r2 = v2;
// p2底层const, p3既是顶层const也是底层const,r2是底层const
2.31 在2.30的基础上,判断下面的语句合法,说明顶层const或底层const的体现
r1 = v2; // 合法,赋值语句,相当于将v2的值赋给v1
// 若为定义语句const int &r1 = v2; 则不合法,因为非const引用不可指向常量对象
p1 = p2; // 不合法,p2为底层const
p2 = p1; // 合法
p1 = p3; // 不合法,p3底层const
p3 = p1; // 不合法,p3顶层const
p2 = p3; // 合法,p2和p3均为底层const
2.32 下面的代码是否合法?如果非法,请将其修改正确
int null = 0, *p = null; // 非法
// 若想让p指向null,应该为int null = 0, *p = &null;
// 若想初始化为空指针,应该为int null = 0, *p = nullptr;
2.33 判断运行结果
int 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; // 合法,a为int
b = 42; // 合法,auto忽略顶层const,b为int
c = 42; // 合法,auto忽略顶层const,c为int
d = 42; // 非法,d为int*
e = 42; // 非法,e为const int*,指向int常量的指针
g = 42; // 非法,g为绑定const int的引用
2.34 同上
2.35 判断下列定义推断出的类型是什么,编写程序进行验证
const int i = 42; // const int
auto j = i; // int
const auto &k = i; // 指向const int的引用
auto *p = &i; // 指向const int的指针
const auto j2 = i, &k2 = i; // j2为const int, k2为指向const int的引用
2.36 指出下列代码每个变量的类型,及程序结束时它们各自的值
int a = 3, b = 4; // a, b为int
decltype(a) c = a; // c为int
decltype((b)) d = a;// d为指向int的引用
++c;
++d; // 程序结束时,a, b, c, d均为4
2.37 赋值是会产生引用的表达式,引用的类型为左值的类型
int a = 3, b = 4; // a, b为int
decltype(a) c = a; // c为指向int的引用
decltype(a = b) d = a; // d为指向int的引用,其中decltype只返回赋值表达式的类型,不会执行a = b
// a, c, d为3, b为4
2.38 说明由decltype指定类型和由auto指定类型有何区别
const int a = 1;
auto b = a; // b为int型
decltype(a) c = 1; // c为const int型
2.39 编译程序并运行,查看相关信息
struct Foo {} // 没有分号, 无法编译通过
int main(){
return 0;
}