为什么指针是许多刚开始学习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).
我认为大学为帮助学生学习指针所能做的最好的事情就是使用良好的演示,并结合需要使用指针的项目。对于指针的理解,一个困难的项目要比上千个演示做得多。演示可以让您对指针有一个浅显的理解,但要深刻地理解指针,您必须真正地使用它们。
其他回答
这种混淆来自于在“指针”概念中混合在一起的多个抽象层。程序员不会对Java/Python中的普通引用感到困惑,但指针的不同之处在于它们暴露了底层内存架构的特征。
清晰地分离抽象层是一个很好的原则,而指针做不到这一点。
The problem with pointers is not the concept. It's the execution and language involved. Additional confusion results when teachers assume that it's the CONCEPT of pointers that's difficult, and not the jargon, or the convoluted mess C and C++ makes of the concept. So vast amounts of effort are poored into explaining the concept (like in the accepted answer for this question) and it's pretty much just wasted on someone like me, because I already understand all of that. It's just explaining the wrong part of the problem.
为了让你知道我是怎么来的,我是一个非常理解指针的人,我可以在汇编语言中熟练地使用它们。因为在汇编语言中,它们不被称为指针。它们被称为地址。当我在C语言中编程和使用指针时,我犯了很多错误,并感到非常困惑。我还没弄清楚。让我给你们举个例子。
当一个api说:
int doIt(char *buffer )
//*buffer is a pointer to the buffer
它想要什么?
它可能想要:
表示缓冲区地址的数字
(给它,我说doIt(mybuffer),还是doIt(* mybuffer) ?)
表示缓冲区地址的一种数字
(doIt(&mybuffer) or doIt(mybuffer) or doIt(*mybuffer)?)
表示缓冲区地址的地址的数字
(可能是doIt(&mybuffer)。还是doIt(&&mybuffer) ?甚至doIt(&&&mybuffer))
and so on, and the language involved doesn't make it as clear because it involves the words "pointer" and "reference" that don't hold as much meaning and clarity to me as "x holds the address to y" and "this function requires an address to y". The answer additionally depends on just what the heck "mybuffer" is to begin with, and what doIt intends to do with it. The language doesn't support the levels of nesting that are encountered in practice. Like when I have to hand a "pointer" in to a function that creates a new buffer, and it modifies the pointer to point at the new location of the buffer. Does it really want the pointer, or a pointer to the pointer, so it knows where to go to modify the contents of the pointer. Most of the time I just have to guess what is meant by "pointer" and most of the time I'm wrong, regardless of how much experience I get at guessing.
指针重载了。指针是指向值的地址吗?或者它是一个将地址保存到值的变量。当一个函数需要一个指针时,它是想要指针变量保存的地址,还是指针变量的地址? 我困惑。
在C/ c++语言中,指针为什么是许多新、甚至老大学生困惑的主要因素?
一个值的占位符的概念——变量——映射到我们在学校教的东西——代数。如果不理解内存在计算机中是如何物理布局的,就无法画出一个现有的并行图,而且没有人会考虑这种事情,直到他们处理低级别的事情——在C/ c++ /字节通信级别。
有没有什么工具或思维过程可以帮助你理解指针在变量、函数和级别之外是如何工作的?
地址框。我记得当我学习在微型计算机上编程BASIC时,有一些漂亮的书,里面有游戏,有时你必须在特定的地址中插入值。他们有一张图片,上面有一堆盒子,标有0、1、2……它解释说,只有一个小的东西(一个字节)可以装在这些盒子里,而它们有很多——一些计算机有多达65535!他们紧挨着,都有一个地址。
有什么好的实践可以让人达到“啊哈,我懂了”的水平,而不会让他们陷入整体概念中?基本上,就是模拟场景。
为了演习?创建一个结构体:
struct {
char a;
char b;
char c;
char d;
} mystruct;
mystruct.a = 'r';
mystruct.b = 's';
mystruct.c = 't';
mystruct.d = 'u';
char* my_pointer;
my_pointer = &mystruct.b;
cout << 'Start: my_pointer = ' << *my_pointer << endl;
my_pointer++;
cout << 'After: my_pointer = ' << *my_pointer << endl;
my_pointer = &mystruct.a;
cout << 'Then: my_pointer = ' << *my_pointer << endl;
my_pointer = my_pointer + 3;
cout << 'End: my_pointer = ' << *my_pointer << endl;
与上面的例子相同,除了在C中:
// Same example as above, except in C:
struct {
char a;
char b;
char c;
char d;
} mystruct;
mystruct.a = 'r';
mystruct.b = 's';
mystruct.c = 't';
mystruct.d = 'u';
char* my_pointer;
my_pointer = &mystruct.b;
printf("Start: my_pointer = %c\n", *my_pointer);
my_pointer++;
printf("After: my_pointer = %c\n", *my_pointer);
my_pointer = &mystruct.a;
printf("Then: my_pointer = %c\n", *my_pointer);
my_pointer = my_pointer + 3;
printf("End: my_pointer = %c\n", *my_pointer);
输出:
Start: my_pointer = s
After: my_pointer = t
Then: my_pointer = r
End: my_pointer = u
也许这通过例子解释了一些基础知识?
我喜欢家庭地址的比喻,但我一直认为地址是邮箱本身。通过这种方式,您可以可视化解除指针引用(打开邮箱)的概念。
例如在一个链表下面: 1)论文开头写上地址 2)去纸上的地址 3)打开邮箱,找到一张新纸,上面写着下一个地址
在线性链表中,最后一个邮箱中没有任何内容(列表的末尾)。在循环链表中,最后一个邮箱具有其中第一个邮箱的地址。
请注意,第3步是发生解引用的地方,当地址无效时,您将崩溃或出错。假设你可以走到一个无效地址的邮箱前,想象那里有一个黑洞或什么东西,把世界翻个底朝天:)
我认为理解指针的主要障碍是糟糕的老师。
几乎每个人都被教导过关于指针的谎言:它们只不过是内存地址,或者它们允许你指向任意位置。
当然,他们很难理解,危险,半魔法。
这些都不是真的。指针实际上是相当简单的概念,只要你坚持c++语言对它们的描述,不要给它们灌输“通常”在实践中会起作用的属性,但语言却不能保证,因此不是指针实际概念的一部分。
几个月前,我试图在这篇博客文章中对此进行解释——希望它能帮助到一些人。
(请注意,在有人对我说学究气之前,是的,c++标准确实说指针表示内存地址。但它并没有说“指针是内存地址,而且只是内存地址,可以与内存地址互换使用或认为”。区别很重要)