2.3 复合类型
2.3.1 引用
练习2.15
下面哪个定义是不合法的?为什么?
( a ) int ival = 1.01;
( b )int &rvall = 1.01;
( c )int &rvall2 = ival;
( d )int &rval3;
(b)是非法的,引用必须指向一个实际存在的对象而非字面值常量。
(d)是非法的,无法另引用重新绑定到另外一个对象上,所以引用必须初始化。
(a)和(c)是合法的。
练习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)是合法的,为引用赋值实际上是把值赋给了与引用和绑定的对象,在这里3.14159赋给了d。
(b)是合法的,以引用作为初始值实际上是以引用绑定的对象作为初始值,在这里将i的值赋给了变量d。会执行自动转换(int -> double)
(c)是合法的,把d的值赋给了变量i,因为d是双精度浮点数而i是整数,所以该语句实际上执行了窄化操作。
(d)是合法的,把d的值赋给了变量i,与上一条语句一样执行了窄化操作。
#include <iostream>
using namespace std;
int main() {
int i = 0, &r1 = i;
double d = 0, &r2 = d;
cout << "When r2 = 3.14159:" << endl;
r2 = 3.14159;
cout << "d = " << d << endl;
cout << "r1 = " << r1 << endl;
cout << "r2 = " << r2 << endl;
cout << "i = " << i << endl;
cout << "When r2 = r1:" << endl;
r2 = r1;
cout << "d = " << d << endl;
cout << "r1 = " << r1 << endl;
cout << "r2 = " << r2 << endl;
cout << "i = " << i << endl;
cout << "When i = r2:" << endl;
i = r2;
cout << "d = " << d << endl;
cout << "r1 = " << r1 << endl;
cout << "r2 = " << r2 << endl;
cout << "i = " << i << endl;
cout << "When r1 = d:" << endl;
r1 = d;
cout << "d = " << d << endl;
cout << "r1 = " << r1 << endl;
cout << "r2 = " << r2 << endl;
cout << "i = " << i << endl;
}
// When r2 = 3.14159:
// d = 3.14159
// r1 = 0
// r2 = 3.14159
// i = 0
// When r2 = r1 :
// d = 0
// r1 = 0
// r2 = 0
// i = 0
// When i = r2 :
// d = 0
// r1 = 0
// r2 = 0
// i = 0
// When r1 = d :
// d = 0
// r1 = 0
// r2 = 0
// i = 0
练习2.17
执行下面的代码段将输出什么结果?
int i, &ri = i; i = 5; ri = 10; std::cout << i << " " << ri << std::endl;
引用不是对象,它只是为已经存在的对象起了另外一个名字,因此ri实际上是i的别名。在上述程序中,首先将i赋值为5,然后把这个值更新为10。因为ri是i的引用,所以它们输出是一样的。
#include <iostream>
using namespace std;
int main() {
int i, &ri = i;
i = 5; ri = 10;
std::cout << i << " " << ri << std::endl;
}
//10 10
2.3.2 指针
练习2.18
编写代码分别改变指针的值以及指针所指对象的值。
#include<iostream>
using namespace std;
int main()
{
int i= 5, j = 10;
int *p = &i;
cout << p << " " << *p << endl;
p = &j;
cout << p << " " << *p << endl;
*p = 20;
cout << p << " " << *p << endl;
j = 30;
cout << p << " " << *p << endl;
return 0;
}
//0096F74C 5
//0096F740 10
//0096F740 20
//0096F740 30
练习2.19
说明指针和引用的主要区别。
指针和引用都属于符合类型。都与内存中实际存在的对象有联系。指针''指向''内存中的某个对象,而引用''绑定到''内存中的某个对象,它们都实现了对其他对象的间接访问,二者的主要区别主要有两个方面:
(1)指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以指向几个不同的对象;引用不是一个对象,无法令引用重新绑定到另外一个对象。
(2)指针无需在定义时赋初值,和其他内置类型一样,在块作用域内定义的指针没有被初始化,也将拥有一个不确定的值;引用必须在定义时赋初值。
练习2.20
请叙述下面这段代码的作用。
int i = 42; int *p1 = &i; *p1 = *p1 * *p1;
让指针 pi
指向 i
,然后将 i
的值重新赋值为 42 * 42 (1764)。其中第二行的*表示声明一个指针,第三行的*表示解引用运算,即取出指针p1所指对象的值。
#include<iostream>
using namespace std;
int main()
{
int i = 42;
int *p1 = &i;
*p1 = *p1 * *p1;
cout << "*p1 = " << *p1 << endl;
return 0;
}
//*p1 = 1764
练习2.21
请解释下述定义。在这些定义中有非法的吗?如果有,为什么?
int i = 0;
(a) double* dp = &i;
(b) int *ip = i;
(c) int *p = &i;
(a)是非法的,dp是double指针,而i是一个int性变量,类型不匹配。
(b)是非法的,不能将 int 变量赋给指针。正确的做法是通过取址运算&i得到变量i在内存中的地址,然后再将该地址赋给指针。
(c)是合法的。
练习2.22
假设 p 是一个 int 型指针,请说明下述代码的含义。
if (p) // ... if (*p) // ...
指针p作为if语句的条件时,实际检验的是指针本身的值,即指针所指的地址值。 判断 p 是不是一个空指针。
解引用运算符*p作为if语句的条件时,实际检验的是指针所指的对象内容,在上面所指的是指针p所指的int值。如果int值为0,则条件为假,否则,如果该int值不为0,对应条件为真。
#include<iostream>
using namespace std;
int main()
{
int i = 0;
int *p1 = nullptr;
int *p = &i;
if (p1) //检验指针的值(即指针所指对象的地址)
cout << "p1 pass" << endl;
if(p) //检验指针的值(即指针所指对象的地址)
cout << "p pass" << endl;
if(*p) //检验指针所指对象的值
cout << "i pass" << endl;
return 0;
}
//p pass
练习2.23
给定指针 p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,也请说明原因。
练习2.24
在下面这段代码中为什么 p 合法而 lp 非法?
int i = 42; void *p = &i; long *lp = &i;
p是合法的,因为void*是一种特殊的指针类型,可用于存放任意对象的地址。
lp是非法的,上面的例子已经说过,这种属于类型不匹配的错误。
2.3.3理解符合类型的声明
练习2.25
说明下列变量的类型和值。
(a) int* ip, i, &r = i;
(b) int i, *ip = 0;
(c) int* ip, ip2;
(a)ip是一个整型指针,指向一个整型数,它的值是所指整型数再内存中的地址;i是一个整型数;r是一个引用,它绑定了i,可以看作是i的别名,r的值就是i的值。
(b)i是一个整型数;ip是一个整型指针,但是它不指向任何具体的对象,它的值被初始化为0。
(c)ip是一个整型指针,指向一个整型数,它的值是所指整型数再内存中的地址;ip2是一个整型数。
2.4 const限定符
练习2.26
下面哪些语句是合法的?如果不合法,请说明为什么?
(a) const int buf; (b) int cnt = 0; (c) const int sz = cnt; (d) ++cnt; ++sz;
(a)是非法的,const对象必须初始化,可以改为const int buf=12;
(b)是合法的。
(c)是合法的。
(d)是非法的,sz是一个const对象,其值不能被改变,当然不能执行自增操作。
2.4.1 const的引用
2.4.2 const和指针
练习2.27
下面的哪些初始化是合法的?请说明原因。
int i = -1, &r = 0; int *const p2 = &i2; const int i = -1, &r = 0; const int *const p3 = &i2; const int *p1 = &i2; const int &const r2; const int i2 = i, &r = i;
练习2.28
说明下面的这些定义是什么意思,挑出其中不合法的。
int i, *const cp; int *p1, *const p2; const int ic, &r = ic; const int *const p3; const int *p;
练习2.29
假设已有上一个练习中定义的那些变量,则下面的哪些语句是合法的?请说明原因。
i = ic; p1 = p3; p1 = ⁣ p3 = ⁣ p2 = p1; ic = *p3;