37-C++-指针和自由存储空间

4.7 指针和自由存储空间

在第3章的开头,提到了计算机程序在存储数据时必须跟踪的3种基本属性。为了方便,这里再次列出了这些属性:

l 信息存储在何处;

l 存储的值为多少;

l 存储的信息是什么类型。

您使用过一种策略来达到上述目的:定义一个简单变量。声明语句指出了值的类型和符号名,还让程序为值分配内存,并在内部跟踪该内存单元。

下面来看一看另一种策略,它在开发C++类时非常重要。这种策略以指针为基础,指针是一个变量,其存储的是值的地址,而不是值本身。在讨论指针之前,我们先看一看如何找到常规变量的地址。只需对变量应用地址运算符(&),就可以获得它的位置;例如,如果home是一个变量,则&home是它的地址。程序4.14演示了这个运算符的用法。

程序4.14  address.cpp

//address.cpp——using the & opeartor to find addresses
#include<iostream>
int main()
{
	using namespace std;
	int donuts = 6;
	double cups = 4.5;
	cout<<"donuts value = "<<donuts;
	cout<<" and donuts address = "<<&donuts<<endl;
	cout<<"cups value = "<<cups;
	cout<<" and cups address = "<<&cups<<endl;
	return 0;
 } 

下面是该程序在某个系统上的输出:


显示地址时,该实现的cout使用十六进制表示法,因为这是常用于描述内存的表示法(有些实现可能使用十进制表示法)。在该实现中,donuts的存储位置比cups要低。两个地址的差为4。这是有意义的,因为donuts的类型为int,而这种类型使用4个字节。当然,不同系统给定的地址值可能不同。有些系统可能先存储cups,再存储donuts,这样两个地址值的差将为8个字节,因为cups的类型为double。另外,在有些系统中,可能不会将这两个变量存储在相邻的内存单元中。

使用常规变量时,值是指定的量,而地址为派生量。下面来看指针的策略,它是C++内存管理编程理念的核心。

指针与C++基本原理

面向对象编程与传统的过程性编程的区别在于,OOP强调的是在运行阶段(而不是编译阶段)进行决策。运行阶段指的是程序正在运行时,编译阶段指的是编译器将和程序组合起来时。运行阶段决策就好比度假时,选择参观哪些景点取决于天气和当时的心情;而编译阶段决策更像不管在什么条件下,都坚持预先设定的日程安排。

运行阶段决策提供了灵活性,可以根据当时的情况进行调整。例如,考虑为数组分配内存的情况。传统的方式是声明一个数组。要在C++中声明数组,必须指定数组的长度。因此,数组长度在程序编译时就设定好了;这就是编译阶段决策。您可能认为,在80%的情况下,一个包含20个元素的数组足够了,但程序有时需要处理200个元素。为了安全起见,使用了一个包含200个元素的数组。这样,程序在大多数情况下都浪费了内存。OOP通过将这样的决策推迟到运行阶段进行,使程序更灵活。在程序运行后,可以这次告诉它只需要20个元素,而还可以下次告诉它需要205个元素。

总之,使用OOP时,您可能在运行阶段确定数组的长度。为使用这种方法,语言必须允许在程序运行时创建数组。稍后您看会到,C++采用的方法是,使用关键字new请求正确数量的内存以及使用指针来跟踪新分配的内存的位置。

在运行阶段做决策并非OOP独有的,但使用C++编写这样的代码比使用C语言简单。

处理存储数据的新策略刚好相反,将地址视为指定的量,而将值视为派生量。一种特殊类型的变量——指针由于存储值的地址。因此,指针名表示的是地址。*运算符被称为间接值或解除引用运算符,将其应用于指针,可以得到该地址处存储的值(这和乘法使用的符号相同;C++根据上下文来确定所指的是乘法还是解除引用)。例如,假设manly是一个指针,则manly表示的是一个地址,而*manly表示存储在该地址处的值。*manly与常规int变量等效。程序4.15说明了这几点,它还演示了如何声明指针。

程序4.15   pointer.cpp

//pointer.cpp——our first pointer variable
#include<iostream>
int main()
{
	using namespace std;
	int updates = 6;
	int *p_updates;
	p_updates = &updates;
	cout<<"Values : updates = "<<updates;
	cout<<",*p_updates = "<<*p_updates<<endl;
	cout<<"Addresses : &updates = "<<&updates;
	cout<<", p_updates = "<<p_updates<<endl;
	*p_updates = *p_updates + 1;
	cout<<"Now updates = "<<updates<<endl;
	return 0;
 } 

下面是该程序的输出:


从中可知,int变量updates和指针变量p_updates只不过是同一枚硬币的两面。变量updates表示值,并使用&运算符来获得地址;而变量p_updates表示地址,并使用*运算符来获得值,由于p_updates指向updates,因此*p_updatesupdates完全等价。可以像使用int变量那样使用*p_updates。正如程序4.15表明的,甚至可以将值赋给*p_updates。这样做将修改指向的值。即updates

4.7.1 声明和初始化指针

我们来看看如何声明指针。计算机需要跟踪指针指向的值的类型。例如,char的地址与double的地址,看上去没什么两样,但chardouble使用的字节数是不同的,它们存储值时使用的内部格式也不同。因此,指针声明必须指定指针指向的数据的类型。

例如,前一个示例包含这样的声明:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值