为什么指针是许多刚开始学习C或c++,甚至上了大学的学生困惑的主要因素?有没有什么工具或思维过程可以帮助你理解指针在变量、函数和级别之外是如何工作的?

有什么好的实践可以让人达到“啊哈,我懂了”的水平,而不会让他们陷入整体概念中?基本上,就是模拟场景。


当前回答

为了使事情更混乱一点,有时你必须使用句柄而不是指针。句柄是指向指针的指针,因此后端可以移动内存中的内容以整理堆。如果指针在例程中间发生了变化,那么结果是不可预测的,因此您首先必须锁定句柄,以确保没有任何事情发生。

http://arjay.bc.ca/Modula-2/Text/Ch15/Ch15.8.html#15.8.5比我讲得更清楚一点。: -)

其他回答

这种混淆来自于在“指针”概念中混合在一起的多个抽象层。程序员不会对Java/Python中的普通引用感到困惑,但指针的不同之处在于它们暴露了底层内存架构的特征。

清晰地分离抽象层是一个很好的原则,而指针做不到这一点。

我想我应该在这个列表中添加一个类比,当我作为计算机科学导师解释指针时(回到过去),我发现它非常有用;首先,让我们:


做好准备:

考虑一个有3个车位的停车场,这些车位是编号的:

-------------------
|     |     |     |
|  1  |  2  |  3  |
|     |     |     |

在某种程度上,这就像内存位置,它们是连续的和连续的。有点像数组。现在它们中没有汽车,所以它就像一个空数组(parking_lot[3] ={0})。


添加数据

停车场永远不会空着太久……如果有,那就没有意义了,也没有人会去建造。假设随着时间推移,停车场里停满了3辆车,一辆蓝色的,一辆红色的,一辆绿色的

   1     2     3
-------------------
| o=o | o=o | o=o |
| |B| | |R| | |G| |
| o-o | o-o | o-o |

这些车都是同一类型(car),所以一种思考方法是,我们的车是某种数据(比如int),但它们有不同的值(蓝色,红色,绿色;这可以是一个颜色枚举)


进入指针

现在如果我带你到这个停车场,让你给我找一辆蓝色的车,你伸出一根手指,指着点1的一辆蓝色的车。这就像获取一个指针并将其分配给一个内存地址(int *finger = parking_lot)

你的手指(指针)不是我问题的答案。看你的手指什么也不能告诉我,但如果我看你手指指向的地方(取消指针指向),我就能找到我要找的车(数据)。


重新分配指针

现在我可以让你找到一辆红色的车,你可以把你的手指转向一辆新车。现在您的指针(与之前的指针相同)正在向我显示相同类型(汽车)的新数据(可以找到红色汽车的停车位)。

指针在物理上没有变化,它仍然是你的手指,只是它显示给我的数据变了。(“车位”地址)


双指针(或指向指针的指针)

这也适用于多个指针。我可以问指向红色汽车的指针在哪里,你可以用另一只手用一根手指指向第一个手指。(这就像int **finger_two = &finger)

现在如果我想知道蓝色的车在哪里,我可以顺着食指的方向到第二根手指,到那辆车(数据)。


悬空指针

现在让我们假设你感觉自己很像一座雕像,你想一直用手指着那辆红色的车。如果那辆红色汽车开走了怎么办?

   1     2     3
-------------------
| o=o |     | o=o |
| |B| |     | |G| |
| o-o |     | o-o |

你的指针仍然指向红色汽车的位置,但它已经不在了。假设一辆新车停在那里……一辆橙色的汽车。现在如果我再问你,“红色的车在哪里”,你仍然指着那里,但现在你错了。那不是红色的车,那是橙色的。


指针的算术

好的,你仍然指着第二个停车位(现在被橙色车占据了)

   1     2     3
-------------------
| o=o | o=o | o=o |
| |B| | |O| | |G| |
| o-o | o-o | o-o |

我现在有个新问题…我想知道下一个停车位的车是什么颜色。你可以看到你指向点2,所以你只要加1,你就指向下一个点。(手指+1),现在因为我想知道那里的数据是什么,你必须检查那个点(不仅仅是手指),所以你可以遵从指针(*(手指+1)),以看到那里有一辆绿色的汽车(该位置的数据)

我不认为指针本身令人困惑。大多数人都能理解这个概念。现在你能想到多少个指针或者你能适应多少个间接层次。不需要太多就能让人崩溃。它们可能会被程序中的错误意外更改,这也会使它们在代码中出错时很难调试。

我认为,使指针难以学习的原因是,直到你熟悉了指针的概念,即“在这个内存位置是一组表示int型,double型,字符等的位”。

当你第一次看到一个指针时,你并不知道那个内存位置上有什么。“什么意思,里面有地址?”

我不同意“要么得到要么得不到”的观点。

当你开始发现它们的真正用途时(比如不要将大结构传递到函数中),它们就会变得更容易理解。

指针的复杂性超出了我们可以轻易教授的范围。让学生们互相指指点点和使用写有家庭住址的纸都是很好的学习工具。他们在介绍基本概念方面做得很好。事实上,学习指针的基本概念对于成功使用指针是至关重要的。然而,在产品代码中,通常会遇到比这些简单演示所能封装的复杂得多的场景。

我参与过的系统中,我们有一个结构指向另一个结构指向另一个结构。其中一些结构还包含嵌入式结构(而不是指向其他结构的指针)。这就是指针真正令人困惑的地方。如果你有多个间接层,你最终会得到这样的代码:

widget->wazzle.fizzle = fazzle.foozle->wazzle;

它很快就会让人感到困惑(想象更多的线,可能还有更多的关卡)。再加上指针数组和节点到节点的指针(树、链表),情况就更糟了。我曾见过一些非常优秀的开发人员在开始开发这样的系统时迷失了方向,甚至是那些非常了解基础知识的开发人员。

Complex structures of pointers don't necessarily indicate poor coding, either (though they can). Composition is a vital piece of good object-oriented programming, and in languages with raw pointers, it will inevitably lead to multi-layered indirection. Further, systems often need to use third-party libraries with structures which don't match each other in style or technique. In situations like that, complexity is naturally going to arise (though certainly, we should fight it as much as possible).

我认为大学为帮助学生学习指针所能做的最好的事情就是使用良好的演示,并结合需要使用指针的项目。对于指针的理解,一个困难的项目要比上千个演示做得多。演示可以让您对指针有一个浅显的理解,但要深刻地理解指针,您必须真正地使用它们。