为什么指针是许多刚开始学习C或c++,甚至上了大学的学生困惑的主要因素?有没有什么工具或思维过程可以帮助你理解指针在变量、函数和级别之外是如何工作的?
有什么好的实践可以让人达到“啊哈,我懂了”的水平,而不会让他们陷入整体概念中?基本上,就是模拟场景。
为什么指针是许多刚开始学习C或c++,甚至上了大学的学生困惑的主要因素?有没有什么工具或思维过程可以帮助你理解指针在变量、函数和级别之外是如何工作的?
有什么好的实践可以让人达到“啊哈,我懂了”的水平,而不会让他们陷入整体概念中?基本上,就是模拟场景。
当前回答
指针的复杂性超出了我们可以轻易教授的范围。让学生们互相指指点点和使用写有家庭住址的纸都是很好的学习工具。他们在介绍基本概念方面做得很好。事实上,学习指针的基本概念对于成功使用指针是至关重要的。然而,在产品代码中,通常会遇到比这些简单演示所能封装的复杂得多的场景。
我参与过的系统中,我们有一个结构指向另一个结构指向另一个结构。其中一些结构还包含嵌入式结构(而不是指向其他结构的指针)。这就是指针真正令人困惑的地方。如果你有多个间接层,你最终会得到这样的代码:
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).
我认为大学为帮助学生学习指针所能做的最好的事情就是使用良好的演示,并结合需要使用指针的项目。对于指针的理解,一个困难的项目要比上千个演示做得多。演示可以让您对指针有一个浅显的理解,但要深刻地理解指针,您必须真正地使用它们。
其他回答
我不明白指针有什么好困惑的。它们指向内存中的一个位置,也就是存储内存地址的位置。在C/ c++中,你可以指定指针所指向的类型。例如:
int* my_int_pointer;
表示my_int_pointer包含指向包含int类型的位置的地址。
指针的问题在于它们指向内存中的某个位置,因此很容易回溯到不应该在的某个位置。作为证明,看看C/ c++应用程序中大量的安全漏洞,这些漏洞来自缓冲区溢出(指针的增量超过分配的边界)。
我认为,使指针难以学习的原因是,直到你熟悉了指针的概念,即“在这个内存位置是一组表示int型,double型,字符等的位”。
当你第一次看到一个指针时,你并不知道那个内存位置上有什么。“什么意思,里面有地址?”
我不同意“要么得到要么得不到”的观点。
当你开始发现它们的真正用途时(比如不要将大结构传递到函数中),它们就会变得更容易理解。
我想我应该在这个列表中添加一个类比,当我作为计算机科学导师解释指针时(回到过去),我发现它非常有用;首先,让我们:
做好准备:
考虑一个有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)),以看到那里有一辆绿色的汽车(该位置的数据)
我不认为指针是一个特别棘手的概念——大多数学生的心理模型都映射到这样的东西,一些快速的盒子草图会有帮助。
困难之处在于,至少在我过去的经历和看到其他人处理的过程中,在C/ c++中指针的管理可能是不必要的复杂。
指针让很多人感到困惑的原因是它们大多没有计算机架构背景。由于许多人似乎不知道计算机(机器)是如何实际实现的——在C/ c++中工作似乎很陌生。
一个练习是要求他们实现一个简单的基于字节码的虚拟机(在任何他们选择的语言中,python都很适合这个),其中的指令集集中于指针操作(加载、存储、直接/间接寻址)。然后要求他们为该指令集编写简单的程序。
任何需要比简单加法稍微多一点的东西都会涉及到指针,它们肯定会得到指针。