C++抽象编程——指针(1)——什么是指针?

指针(Pointers)

C ++设计背后的原则之一是程序员应该尽可能多地访问由底层硬件提供的工具。因此,C ++使得内存位置具有程序员可见的地址。其值为内存中的地址的数据项称为指针A data item whose value is an address in memory is called a pointer) 在许多高级编程语言中,指针被通常谨慎使用,因为这些语言提供了消除对指针的大量需求的其他机制。 例如,Java编程语言完全隐藏了编程器的指针。但是在C ++中,指针是无处不在的,在不知道指针如何工作的情况下,不可能理解大多数专业的C ++程序。
在C ++中,使用指针有几个目的,其中以下是最重要的:

  • 指针允许你以简洁的方式引用大型数据结构Pointers allow you to refer to a large data structure in a compact way.)。 程序中的数据结构可能会变得任意大。然而,无论数据结构如何增长,数据结构仍然位于计算机内存中,因此具有一个地址。 指针允许使用地址作为完整值的简写。因为内存地址通常适合四个字节的内存,所以当数据结构本身很大时,这种策略可以节省大量空间。
  • 指针可以在程序执行期间预留新的内存。Pointers make it possible to reserve new memory during program execution)到目前为止,我们可以在程序中使用的唯一内存是分配给明确声明的变量的内存。在许多应用中,程序运行时获取新内存是很方便的,并使用指针来引用该内存。 我们以后会在“动态分配(Dynamic allocation)”讨论了这一方式。
  • 指针可用于记录数据项之间的关系Pointers can be used to record relationships among data items)。在高级编程应用程序中,广泛使用指针来模拟各个数据值之间的连接。例如,程序员通常通过在第一个内部表示中包含指向第二个项目的指针来指示一个数据项目在概念序列中跟随另一个数据项目。使用指针创建各个组件之间的连接的数据结构称为链接结构linked structures)。 链接结构在以后会遇到的许多抽象数据类型中起关键的作用。

使用地址作为数据值

在C ++中,引用可存储数据的,内部存储器位置的,任何表达式称为左值(lvalue,发音为“ell-value”)。 在lvalue开始的 l 来自于左值可以出现在C ++中赋值语句的左侧的观察。例如,简单的变量是左值,因为你可以写一个语句:

x = 1.0;

然而,C ++中的许多值不是左值。 例如,常量不是左值,因为常数不能被更改。 类似地,虽然算术表达式的结果是一个值,但它不是一个左值,因为你不能为算术表达式的结果分配一个新值。

以下属性适用于C ++中的左值:

  • 每个左值都存储在内存中,因此都具有一个地址。
  • 一旦声明,即使这些内存位置的内容可能会更改,左值的地址也不会更改
  • 左值的地址是指针值,可以存储在内存中并作为数据进行操作

声明指针变量

与C ++中的所有其他变量一样,您必须先声明指针变量,然后再使用它们。 要将变量声明为指针,所有你需要做的是在声明中的变量名称之前添加一个星号(* )。 例如:

int *p;

上面这行声明p是指向int的指针类型。 同样,下面的这行:

char *cptr;

声明cptr是指向char的指针类型。 这两种类型,0指针到int和指针到char在C ++中是不同的,即使它们中的每一个在内部被表示为一个地址。要使用该地址的值,编译器需要知道如何解释它,因此需要明确指定其类型。由指针指定的地址处的值称为其目标(target.)。该目标值的类型称为指针的基本类型(base type)。 因此,类型pointer-to-int具有int作为其基本类型。
要注意,用于指示变量是指针的星号符号与变量名称是语法上的,而不是基本类型。如果你使用相同的声明方式来声明相同类型的两个指针,则需要使用星号标记每个变量,如:

int *p1, *p2;

而下面的这样声明:

int *p1, p2;

将p1声明为指向整数的指针,但将p2声明为整数变量.

基本的指针操作

C++定义了两个允许你在指针和目标值之间来回移动的运算符:

&操作符采用左值作为其操作数,并返回存储该值的内存地址。 * 运算符获取任何指针类型的值,并返回它指向的左值。这个操作称为取消引用指针(dereferencing the pointer)。**星号操作产生一个左值,这意味着您可以为取消引用的指针指定一个值。
举个例子去解释指针的使用:

int x, y;
int *p1, *p2;

这些声明为四个字分配内存,两个类型为int,另外两个为pointer-to-int的指针类型。为了具体,我们假设这些值存储在堆栈中,如下图所示的机器地址:

给定这些声明,你可以像往常一样为x和y分配值。例如,执行赋值语句:

x = 42;
y = 163;

导致以下内存状态:

要初始化指针变量p1和p2,你需要分配表示某些整数对象的地址的值。在C ++中,生成地址的运算符是&运算符。可以使用赋值和&操作符使p1指向y和p2指向x,如下所示:

p1 = &y;
p2 = &x;

这些赋值将使内存处于以下状态:

变量p1包含值FF04,它是变量y的地址。 类似地,p2包含值FF00,它是变量x的地址。
使用显式地址来表示指针强调地址作为数字内部存储的事实。然而,它不会让你对指针意味着什么的直观感觉。要完成这个目标,最好用箭头来指示每个指针的目标。如果完全消除了地址值,并使用箭头来表示指针,则该图如下所示:

使用图中的箭头可以清楚地看出,变量p1和p2指向箭头指示的单元格。箭头使得更容易理解指针如何工作,因此以后的大多数存储器图中都会出现箭头。同时,重要的是要记住,指针只是数字地址,机器内部没有箭头。
要从指针移动到指向的值,您可以使用*运算符。 例如,表达式:

*p1

表示p1指向的内存位置的值。 此外,由于p1被声明为指向整数的指针,编译器知道表达式 * p1必须指向一个整数。因此,给定图中所示的存储器的配置,它证明是变量y的另一个名称。

像简单变量名称y一样,表达式* p1是一个左值,您可以为其分配新值。 执行赋值语句:

*p1 = 17;

更改变量y中的值,因为它是指针p1的目标。 完成此赋值后,内存配置为:

也可以为指针变量本身分配新的值。 例如,声明:

p1 = p2;

告诉计算机取包含在变量p2中的值并将其复制到变量p1中。 p2中包含的值是指针值FF00。 如果将该值复制到p1中,p1和p2都包含相同的指针,如下图所示:

只要你记住一个指针具有一个整数的底层表示,复制一个指针的想法根本就不是神秘的。指针的值被简单地拷贝到目的地。如果你使用箭头绘制内存图,则必须记住,复制指针将使用指向与旧位置相同的位置的新箭头替换目标指针。 因此,p1 = p2;的赋值的效果是改变从p1导出的箭头,使其指向与p2起源的箭头相同的位置,如下所示:

将指针的分配与值的分配区分开很重要。 指针分配,如:

p1 = p2;

使p1和p2指向相同的位置。 相比之下,值分配,由下面的语句表示:

*p1 = *p2;

将从p2地址指向的值复制到由p1地址指向的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值