翻译原文:Pointer Basics
这是翻译的斯坦福大学官网上的 CSLibrary 中的一篇课程文档
这个文档介绍指针的基础知识,因为很多计算机语言中都会用到它,例如:C,C++,Java,以及 Pascal。
这是 Stanford CS Education Library 的106号文档。其他免费资料在 cslibrary.stanford.edu 中可以获取到。
1 – 指针规则1
关于指针,好的地方是控制他们工作的规则非常简单。这些规则可以组合得到复杂的结果,但是单个的规则还是很简单。
1) 指针与指针对象
指针存放了对一些东西的引用 。不幸的是,指针指向的目标还没有一个固定的术语,不同的计算机语言中,指针指向的内容很多。我们用术语 指针对象 来代指指针指向的事物,我们就来研究一下那些在所有语言中都正确的 指针/指针对象 关系的基本性质。术语 “引用”和 “指针”的意思非常相似——“引用”意在更高层的讨论,而“指针”是指传统的编译语言对地址指针的实现。对于这里所讲的基本的 指针/指针对象 规则,这两个术语可以认为是等同的。
上面的图中画出了一个名为 x
的指针,指向了一个保存了数值42
的指针对象。指针通常被画成一个矩形盒子,指针中保存的引用被画成一个箭头,从指针的盒子开始,指向它的指针对象。
分配指针和给指针分配指针对象是分开的两个步骤。你可以认为 指针/指针对象 结构是在两个层级的操作。两个层级都必须设置才能正常工作。最常见的错误集中于编写手动操作指针的代码时,忘记了设置指针对象。有时候不接触指针对象的指针操作被称为“浅”,而操作指针对象的指针操作称为“深”。
2) 解引用
解引用 操作从指针开始,沿着箭头一直访问到它的指针对象。目的可能是查看指针对象的状态或者改变指针对象的状态。
对一个指针执行的解引用操作只在这个指针有一个指针对象——这个指针对象必须已经被分配并且指针必须已经设置为指向它—— 的情况下有效。最常见的错误就是代码中忘记为指针设置指针对象。在 Java 中,运行时会礼貌地标记错误的解引用。在编译语言中,例如 C,C++,和Pascal中,错误的解引用有时会崩溃,有时会以一些微妙而随机的方式破坏内存。因此编译语言中的指针异常很难追踪。
3) 指针赋值
两个指针之间的 指针赋值 会使他们指向同一个指针对象。所以 y=x;
的赋值会让 y
指向与 x
相同的指针对象。指针赋值不接触指针对象。它只是改变一个指针来让它拥有与其他指针相同的引用。指针赋值后,这两个指针被称为 共享
指针对象。
2 – 代码示例
有不同计算机语言的代码示例。所有的版本都有相同的结构,证明相同的基本规则和指针只是;他们只有语法上的不同。不考虑特定语言,示例的基本结构如下…
1. 分配 x 和 y 两个指针。让他们不指向任何指针对象。 | |
---|---|
2. 分配一个指针对象,然后设置 x 指向它。每种语言都有自己的语法来实现这一步操作。重要的是指针对象的内存是动态分配的,然后让x 指向这个指针对象 | |
3.解引用 x 将42保存到指针对象中。这是解引用操作的基本示例。从 x 开始,沿着箭头方向访问到指针对象。 | |
4. 尝试解引用 y 来保存 13 到指针对象中.这一步会崩溃——因为它还没有被赋值一个指针对象。 | |
5. 赋值 y=x; 让 y 指向 x 的指针对象。现在 x 和 y 指向了相同的指针对象——他们是共享的。 | |
6. 尝试解引用 y 来保存 13到指针对象中。这一次成功了,因为上一步赋值操作为 y 分配了指针对象。 |
版本
下面是不同语言版本的示例。他们做了相同的事情——只是每种语言的语法不同。
C 版本
指针 x
和 y
分配成本地变量。类型 int*
表示“指针指向 int 类型”。指针不会自动得到指针对象。x
的指针对象是另外用标准库 malloc()
动态分配的。语法 *x
解引用 x 来访问它的指针对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void main() {
int* x; // Allocate the pointers x and y
int* y; // (but not the pointees)
x = malloc(sizeof(int)); // Allocate an int pointee,
// and set x to point to it
*x = 42; // Dereference x to store 42 in its pointee
*y = 13; // CRASH -- y does not have a pointee yet
y = x; // Pointer assignment sets y to point to x's pointee
*y = 13; // Dereference y to store 13 in its (shared) pointee
}
C语言(或者C++)中另一种使用指针的方式是使用 & 符号(ampersand )运算符来计算栈中指向本地内存的指针。然而,指针对象通常是在堆中动态分配的,所以这就是我们展示的。
Java 版本
Java中最常见的 指针/指针对象结构是一个本地变量作为指针,指向一些类的对象作为指针对象。所以为了按计划创建一个保存了整数类型的指针对象,我们定义一个 IntObj
类来保存一个整型。然后我们可以创建一个 IntObj
指针对象来保存 int。用 IntObj x;
这样的代码来分配指针不会自动分配指针对象。 IntObj
指针对象通过调用 new
来分配。x.value
解引用 x
来访问指针对象中的 .value
字段。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class IntObj {
public int value;
}
public class Binky() {
public static void main(String[] args) {
IntObj x; // Allocate the pointers x and y
IntObj y; // (but not the IntObj pointees)
x = new IntObj(); // Allocate an IntObj pointee
// and set x to point to it
x.value = 42; // Dereference x to store 42 in its pointee
y.value = 13; // CRASH -- y does not have a pointee yet
y = x; // Pointer assignment sets y to point to x's pointee
y.value = 13; // Deference y to store 13 in its (shared) pointee
}
}
C++ 版本
与 上面 C 语言版本唯一的不同是使用了标准操作符 new
来代替 malloc()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void main() {
int* x; // Allocate the pointers x and y
int* y; // (but not the pointees)
x = new int; // Allocate an int pointee,
// and set x to point to it
*x = 42; // Dereference x to store 42 in its pointee
*y = 13; // CRASH -- y does not have a pointee yet
y = x; // Pointer assignment sets y to point to x's pointee
*y = 13; // Dereference y to store 13 in its (shared) pointee
}
Pascal 版本
这跟 C 语言版本在结构一致,但是使用了 Pascal 语法。^Integer
意思是“指向整型的指针”。分配指针不会自动分配它的指针对象。标准程序 New()
取一个指针参数,分配一个新的指针对象,设置指针指向这个指针对象。表达式 x^
解引用访问这个指针对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14Procedure main
var x:^Integer; /* Allocate the pointers x and y */
var y:^Integer; /* (but not the pointees) */
Begin
New(x); /* Allocate a pointee and set x to point to it */
x^ := 42; /* Deference x to store 42 in its pointee */
y^ := 13; /* CRASH -- y does not have a pointee yet */
y := x; /* Pointer assignment makes y point to x's pointee */
y^ := 13; /* Dereference y to store 13 in its (shared) pointee */
End;
3 – 习题
这些习题考察指针的基本特性。其中两个问题大量使用内存图。内存图是思考指针问题的很好的方法。
Question 1
在上面代码的末尾,y
被指定了一个指针对象,然后解引用它保存数字13到指针对象中,在这发生后,x
的指针对象的值是多少?
答案:13,因为 x
的指针对象也是 y
的指针对象,这就是指针共享的全部作用——多个指针指向一个指针对象。
Question 2
思考下面这张图…
选择中语言,写代码创建上面的指针结构。
答案:基本步骤是…
- 分配两个指针
- 分配两个指针对象,然后设置两个指针分别指向他们
- 保存数字 1 和 2 到这两个指针对象中
- 赋值第一个指针指向第二个指针对象。这会丢失对第一个指针对象的引用,这是很少见的,但题目就是这样要求的。
C Code1
2
3
4
5
6
7
8
9{
int* x;
int* y;
x = malloc(sizeof(int));
y = malloc(sizeof(int));
*x = 1;
*y = 2;
x = y;
}
Java Code1
2
3
4
5
6
7
8
9{
IntObj x;
IntObj y;
x = new IntObj();
y = new IntObj();
x.value = 1;
y.value = 2;
x = y;
}
Question 3
假设你有一个指针对象叫作 “Node”,包含两个东西,一个 int 和一个 指向另一个 Node 的指针(这个Node 类型的的定义在下面)。你可以用这样的指针对象类型在一个结构中构造三个 Node 互相指向的指针对象,像这样…
名叫 x
的指针指向第一个 Node 指针对象。第一个 Node 包含一个指针指向第二个 Node ,第二个包含一个指针指向第三个 Node ,第三个 Node 又包含一个指针指向了第一个 Node。这个结构只能用我们已经见过的指针对象分配,解引用和赋值的规则来创建。使用下面的定义,每一个 Node 都包含了一个 Integer 名为 value
,和一个指针指向另一个名为 next
的 Node。
C Code1
2
3
4struct Node {
int value;
struct Node* next;
};
Java Code1
2
3
4class Node {
public int value;
public Node next;
};
写代码创建上面图中的结构。为了方便,你可以使用 x
以外的临时指针来访问指针对象中的字段——所以 ->value
访问 x
指针对象中的 名为value
字段。
Answer The basic steps are… 答案 基本步骤…
- 分配三个指针:
x
指向第一个 Node,然后临时指针y
和z
指向其他两个 Node。 - 分配三个 Node 指针对象并保存他们的解引用到三个指针中。
- 解引用每一个指针对象,保存合适的数值到其指针对象的
value
字段中。 - 解引用每个指针来访问其指针对象中的
.next
字段,并使用指针赋值来设置.next
字段指向合适的 Node。
C Code1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18{
// Allocate the pointers
struct Node* x;
struct Node* y;
struct Node* z;
// Allocate the pointees
x = malloc(sizeof(Node));
y = malloc(sizeof(Node));
z = malloc(sizeof(Node));
// Put the numbers in the pointees
x->value = 1;
y->value = 2;
z->value = 3;
// Put the pointers in the pointees
x->next = y;
y->next = z;
z->next = x;
}
Java Code1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18{
// Allocate the pointers
Node x;
Node y;
Node z;
// Allocate the pointees
x = new Node();
y = new Node();
z = new Node();
// Put the numbers in the pointees
x.value = 1;
y.value = 2;
z.value = 3;
// Put the pointers in the pointees
x.next = y;
y.next = z;
z.next = x;
}
这里介绍的 Node 结构实际上是一种真实的数据类型,被用来创建 “链表” 数据结构。链表是指针的实际应用的用途,是提升指针技能的绝佳领域。看Stanford CS Library 中的Linked List Basics 和 Linked List Problems 了解更多链表知识。
Up to the CS Education Library Home