人们对C指针有什么困难?

从这里发布的问题的数量来看,很明显,人们在指针和指针算术的头脑中有一些非常有意思的问题。

我很想知道为什么。 他们从来没有真正引起我的重大问题(虽然我第一次了解到他们在新石器时代)。 为了给这些问题写出更好的答案,我想知道人们觉得困难。

所以,如果你正在用指针挣扎,或者你最近突然“知道了”,指针的哪些方面会给你带来问题?

我怀疑人们的答案有点太深。 对调度,实际CPU操作或汇编级内存pipe理的理解并不是真正需要的。

当我在教书的时候,我发现以下漏洞让学生的理解成为最常见的问题来源:

  1. 堆vs堆栈存储。 简单地说,即使在一般意义上,有多less人不明白这一点。
  2. 堆栈帧。 只是局部variables栈的专用部分的一般概念,以及它是“栈”的原因…诸如隐藏返回位置,exception处理程序细节和先前寄存器的细节可以安全地留下,直到有人试图build立一个编译器。
  3. “内存就是内存”内存只是改变了操作符的版本或者编译器为特定的内存空间提供了多less空间。 当人们谈论“什么(原始)variablesX 真的是”时,你就知道你正在处理这个问题。

我的大多math生都能够理解一个内存块的简化绘图,通常是当前范围内堆栈的局部variables部分。 通常给各个地点明确的虚构地址有所帮助。

总而言之,我想说如果你想了解指针,你必须了解variables,以及它们在现代架构中的实际情况。

当我第一次和他们合作的时候,我遇到的最大的问题是语法。

int* ip; int * ip; int *ip; 

都一样。

但:

 int* ip1, ip2; //second one isn't a pointer! int *ip1, *ip2; 

为什么? 因为声明的“指针”部分属于variables,而不是types。

然后解引用这个东西使用一个非常类似的符号:

 *ip = 4; //sets the value of the thing pointed to by ip to '4' x = ip; //hey, that's not '4'! x = *ip; //ahh... there's that '4' 

除非你真的需要一个指针,那么你使用&符号!

 int *ip = &x; 

万岁的一致性!

然后,很明显,只是为了抽象,certificate它们是多么聪明,许多库开发人员使用指针指针指针,如果他们期望有一些这样的事情,那么为什么不传递一个指针呢。

 void foo(****ipppArr); 

要调用这个,我需要指向指向int的指针的指针数组地址:

 foo(&(***ipppArr)); 

在六个月的时间里,当我要维护这些代码的时候,我会花更多的时间来弄清楚这些意义,而不是从头开始重写。 (是的,可能是这个语法错了 – 我已经用C做了任何事情已经有一段时间了。我有点想念它,但是后来我变成了一个群众主义者)

正确理解指针需要了解底层机器的体系结构。

现在许多程序员不知道他们的机器是如何工作的,就像大多数知道如何驾驶汽车的人对发动机不了解。

在处理指针时,感到困惑的人广泛地在两个阵营之一。 我一直在(都?)。

array[]人群

这是直线上的人群不知道如何从指针表示法转换为数组表示法(甚至不知道它们甚至是相关的)。 以下是访问数组元素的四种方法:

  1. 数组符号(索引)与数组名称
  2. 数组符号(索引)与指针名称
  3. 指针符号(*)与指针名称
  4. 指针表示法(*)与数组名称
 int vals[5] = {10, 20, 30, 40, 50}; int *ptr; ptr = vals; array element pointer notation number vals notation vals[0] 0 10 *(ptr + 0) ptr[0] *(vals + 0) vals[1] 1 20 *(ptr + 1) ptr[1] *(vals + 1) vals[2] 2 30 *(ptr + 2) ptr[2] *(vals + 2) vals[3] 3 40 *(ptr + 3) ptr[3] *(vals + 3) vals[4] 4 50 *(ptr + 4) ptr[4] *(vals + 4) 

这里的想法是通过指针访问数组看起来非常简单直接,但是通过这种方式可以完成许多非常复杂和巧妙的事情。 其中一些可能让有经验的C / C ++程序员感到迷惑,更不用说没有经验的新手了。

reference to a pointerreference to a pointer pointer to a pointer

这是一个伟大的文章,解释了这个差异,我将引用和窃取一些代码:)

作为一个小例子,如果遇到类似这样的事情,可能很难看出作者想要做什么:

 //function prototype void func(int*& rpInt); // I mean, seriously, int*& ?? int main() { int nvar=2; int* pvar=&nvar; func(pvar); .... return 0; } 

或者,在较小的程度上,这样的事情:

 //function prototype void func(int** ppInt); int main() { int nvar=2; int* pvar=&nvar; func(&pvar); .... return 0; } 

所以在这一天结束的时候,我们真的用这些乱码解决了什么问题? 没有。

现在我们已经看到了ptr-to-ptr和ref-to-ptr的语法。 相互之间有什么优势吗? 我害怕,不。 对于一些程序员来说,两者之一的使用只是个人喜好。 一些使用ref-to-ptr的人说,语法是“更干净”的,而一些使用ptr-to-ptr的人,比如说ptr-to-ptr的语法会使读者更清楚你正在做什么。

这种复杂性和看似 (大胆似乎)与引用的互换性,这往往是指针的另一个警告和新手的错误,使得理解指针困难。 为了完成起见,理解指向引用的指针在C和C ++中是非法的,因为混淆的理由将其引入lvaluervalue语义。

正如前面的回答所指出的那样,很多时候你只会让这些炙手可热的程序员觉得他们很聪明,通过使用******awesome_var->lol_im_so_clever() ,我们大多数人可能有时会写这样的暴行,但这只是不好的代码,而且肯定是不可维护的。

那么这个答案竟然比我想象的要长…

我亲自指责参考资料和做教学的人员的素质; C中的大多数概念( 特别是指针)只是简单的教导 。 我一直威胁写我自己的C书(标题为“最后的事情世界需要另一本关于C编程语言的书” ),但是我没有时间和耐心来这样做。 所以我在这里闲逛,从人群中随意的引用标准。

还有一个事实是,当C最初被devise的时候,人们认为你把机器的体系结构理解得相当详细,因为在你的日常工作中没有办法避免它(内存太紧,处理器太慢你必须了解你写的是如何影响性能的)。

有一篇很好的文章支持Joel Spolsky的网站–JavaSchools的风险指针的概念。

[免责声明 – 我本人不是Java仇恨者]

如果你不是基于“底层”的知识,那么大多数事情都是难以理解的。 当我教CS的时候,当我开始学习编程一个非常简单的“机器”,一个带十进制操作码的模拟十进制计算机,其内存由十进制寄存器和十进制地址组成时,它变得更容易了。 他们会投入很短的程序,例如,添加一系列数字来获得总数。 然后,他们会单步一步看看发生了什么事情。 他们可以按住“input”键并看着它“快速”运行。

我敢肯定,几乎所有人都想知道为什么要这么基本。 我们忘记了不知道如何编程。 用这样的玩具电脑玩这个概念,没有这个概念就不能编程,比如计算是一步一步的过程,使用less量的基本原语来构build程序,以及内存的概念variables作为存储数字的地方,其中variables的地址或名称与其所包含的数字不同。 你input程序的时间和“运行”的时间是有区别的。 我把学习比喻为跨越一系列“速度颠簸”,比如非常简单的程序,然后是循环和子程序,然后是数组,然后是顺序I / O,然后是指针和数据结构。 所有这些都是通过参考电脑真正在做的事情而更容易学习的。

最后,当进入C时,K&R做了一个非常好的解释他们的指针是困惑的。 我在C中学习的方式是知道如何阅读它们 – 从右到左。 就像当我在头脑中看到int *p ,我会说“ p指向一个int ”。 C是从汇编语言中发展起来的,这就是我喜欢的东西 – 接近于“地面”。 指针和其他任何东西一样,如果你没有这个基础,就很难理解。

在阅读K&R中的描述之前,我没有得到指针。 在这之前,指针没有意义。 我读了一大堆东西,人们说:“不要学习指针,他们混乱,会伤害你的脑袋,给你动脉瘤”,所以我远离了它,造成了这个不必要的困难概念。

否则,大部分我认为的是,为什么你要一个variables,你必须通过箍来获得价值,如果你想分配的东西,你必须做一些奇怪的事情,才能获得价值进入他们。 我想,variables的整个意义在于存储一个值,所以为什么有人想让它变得复杂,就超出了我的意思。 “所以用一个指针,你必须使用*运算符来获得它的值,这是一个什么样的愚蠢variables?” , 我想。 毫无意义,没有双关意图。

原因很复杂,因为我不明白指针是什么东西的地址 。 如果你解释说这是一个地址,它是包含一个地址的东西,而且你可以操纵这个地址做有用的事情,我想这可能会消除混淆。

一个需要使用指针访问/修改PC上的端口的类,使用指针algorithm来寻址不同的内存位置,并查看更复杂的C代码来修改他们的观点,使我不能理解指针是没有意义的。

这是一个指针/数组的例子,让我暂停。 假设你有两个数组:

 uint8_t source[16] = { /* some initialization values here */ }; uint8_t destination[16]; 

您的目标是使用memcpy()从源目标复制uint8_t内容。 猜猜以下哪一项达到了这个目标:

 memcpy(destination, source, sizeof(source)); memcpy(&destination, source, sizeof(source)); memcpy(&destination[0], source, sizeof(source)); memcpy(destination, &source, sizeof(source)); memcpy(&destination, &source, sizeof(source)); memcpy(&destination[0], &source, sizeof(source)); memcpy(destination, &source[0], sizeof(source)); memcpy(&destination, &source[0], sizeof(source)); memcpy(&destination[0], &source[0], sizeof(source)); 

答案(扰stream警报!)是所有的。 “destination”,“&destination”和“&destination [0]”都是相同的值。 “&destination”与另外两个types是不同的types ,但它仍然是相同的值。 “源”的排列也是一样的。

另外,我个人比较喜欢第一个版本。

我应该首先说C和C ++是我学到的第一种编程语言。 我从C开始,然后在学校里做了很多C ++,然后回到C来stream利。

学习C的时候,第一件让我困惑的事情就是简单:

 char ch; char str[100]; scanf("%c %s", &ch, str); 

这种混淆大部分是基于在向我正确引入指针之前引用了用于OUT参数的variables的引用。 我记得我没有写C语言中的头几个例子,因为它们太简单了,只是没有得到我写的第一个程序(很可能是因为这个)。

令人困惑的是这个问题实际上意味着什么,以及为什么str不需要它。

在我熟悉了之后,我记得我对dynamic分配感到困惑。 我意识到,在没有dynamic分配某种types的情况下,指向数据并不是非常有用,所以我写了如下的东西:

 char * x = NULL; if (y) { char z[100]; x = z; } 

尝试dynamic分配一些空间。 它没有工作。 我不确定它会起作用,但我不知道它可能会如何工作。

后来我了解了mallocnew ,但是对我来说,他们真的像是不可思议的内存生成器。 我对他们如何工作一无所知。

一段时间后,我再次被教授recursion(我以前自己学过,但现在在课上),我问了它是如何工作的 – 在哪里存储单独的variables。 我的教授说“在栈上”,很多事情都清楚了。 我之前听说过这个词,之前已经实现了软件栈。 早已听到别人提到“堆栈”,但却忘记了这一点。

大约在这个时候,我也意识到在C中使用multidimensional array会变得非常混乱。 我知道他们是如何工作的,但是他们很容易混淆,所以我决定尽可能地解决问题。 我认为这里的问题主要是句法(特别是从函数传递或返回)。

自从我为未来一两年的C ++写作学校以来,我有了很多使用数据结构指针的经验。 在这里,我遇到了一系列新的麻烦 – 混合指针。 我会有多层次的指针(像node ***ptr;这样的东西),并且总是绊倒自己。 我会用一个错误的次数去引用一个指针,最后通过反复试验来计算出我需要多less个*

在某个时候,我学到了一个程序堆的工作方式(有点不错,但足够好,以至于在晚上不再让我上class)。 我记得读过,如果你在malloc在某个系统返回的指针之前看几个字节,你可以看到有多less数据被实际分配了。 我意识到malloc中的代码可以从操作系统请求更多的内存,这个内存不是我的可执行文件的一部分。 对malloc工作方式有一个体面的工作思路是非常有用的。

在此之后不久,我参加了一个集会课程,并没有像大多数程序员可能想到的那样教会我多less指针。 但是,它确实让我更多地考虑了我的代码可能被翻译成什么样的程序集。 我一直试图编写高效的代码,但现在我有一个更好的主意如何。

我还参加了几个课,我不得不写一些口齿伶俐的东西 。 在写lisp的时候,我并没有像在C中那样关心它的效率。我很less知道如果编译这个代码可能会被翻译成什么,但是我确实知道它使用了大量的本地命名符号(variables)事情要容易得多。 在某些时候,我在一些lisp中写了一些AVL树轮转代码,因为指针问题,我很难用C ++编写代码。 我意识到我厌恶我认为是多余的局部variables阻碍了我用C ++写这个和其他几个程序的能力。

我也参加了一个编译器课程。 在本课中,我翻阅了先进的材料,学习了静态 单个 赋值 (SSA)和死variables,这不是那么重要,只是它教会了我,任何一个体面的编译器都会做一个体面的工作来处理variables不再使用。 我已经知道,更多的variables(包括指针),正确的types和良好的名字将帮助我把事情保持在我的头脑中,但是现在我也知道,为了效率的原因避免它们比我那些微观优化的教授告诉我更愚蠢我。

所以对于我来说,了解一个程序的内存布局的好处很多。 思考我的代码意味着什么,在硬件上象征性地帮助我。 使用具有正确types的本地指针会有所帮助。 我经常写代码如下所示:

 int foo(struct frog * f, int x, int y) { struct leg * g = f->left_leg; struct toe * t = g->big_toe; process(t); 

所以,如果我搞砸了一个指针types,编译器错误是非常清楚的问题是什么。 如果我做到了:

 int foo(struct frog * f, int x, int y) { process(f->left_leg->big_toe); 

并得到任何指针types错误在那里编译器错误将是一个很难弄清楚很多。 我会试图在我的沮丧中尝试改变错误,可能会让事情变得更糟。

我在C语言的一些电话程序上做了“指针时刻”的工作。我不得不使用一种只能理解经典C的协议分析器来编写一个AXE10交换仿真器。一切都取决于了解指针。 我试着写我的代码没有他们(嘿,我是“前指针”削减了我一些松懈),并失败了。

对我来说理解它们的关键是&(地址)操作符。 一旦我明白了&i意思是“ &i的地址”,那么理解*i意思是“ *i指向的地址的内容”稍后。 每当我写或读我的代码,我总是重复什么“&”的意思和“*”的意思,最终我来直观地使用它们。

令我惭愧的是,我被迫进入了VB和Java,所以我的指针知识并不像以前那么犀利,但我很高兴自己是“后指针”。 不要问我使用一个需要我理解* * p的库。

指针的主要困难,至less对我来说,是我没有从C开始。我从Java开始。 指针的整个概念都是非常陌生的,直到我在大学期间学习了几堂课,然后我才学会了C的基本知识,以及如何在基本意义上使用指针。 即使那样,每当我发现自己正在阅读C代码时,我都必须查找指针语法。

所以在我非常有限的经历(1年的现实世界+4年的大学生涯)中,指针让我感到困惑,因为除了教室之外,我从来没有真正使用过它。 我可以同情现在开始使用JAVA而不是C或C ++的CS。 正如你所说,你在“新石器时代”的时代学到了一些指针,从那以后就可能使用它。 对于我们新来的人来说,分配内存和做指针运算的概念是非常陌生的,因为所有这些语言都把它抽象出来了。

PS在阅读Spolsky散文之后,他对“JavaSchools”的描述与我在康奈尔大学所经历的(“05 -'09”)完全不同。 我使用了结构和函数式编程(sml),操作系统(C),algorithm(笔和纸)以及java中没有教过的其他类。 然而,所有的介绍类和选修课都是用java完成的,因为当你试图做一些比用指针实现散列表更高级别的东西时,没有重新发明轮子的价值。

回头看,有四件事真的帮助我终于明白指针。 在此之前,我可以使用它们,但是我没有完全理解它们。 也就是说,我知道如果我遵循这些forms,我会得到我想要的结果,但是我并没有完全理解forms的“为什么”。 我意识到这不是你所要求的,但我认为这是一个有用的必然结果。

  1. 编写一个指向一个整数并修改整数的例程。 这给了我build立任何指针工作的心智模式的必要forms。

  2. 一维dynamic内存分配 弄清楚一维内存分配使我了解指针的概念。

  3. 二维dynamic内存分配 弄清楚二维内存分配强化了这个概念,同时也教会了我指针本身需要存储和必须考虑的问题。

  4. 堆栈variables,全局variables和堆内存之间的差异。 弄清楚这些差异教会了指针指向的内存types。

这些项目中的每一个都需要想象在较低的层次上发生的事情 – build立一个心理模型,满足每一个我能想到的情况。 这需要时间和精力,但这是非常值得的。 我相信,要理解指针,你必须build立他们如何工作的思维模型,以及如何实现。

现在回到你原来的问题。 根据以前的名单,有几个我原本很难抓的项目。

  1. 如何以及为什么会使用指针。
  2. 他们有什么不同,但与arrays相似。
  3. 了解指针信息的存储位置。
  4. 了解指针所指向的内容和位置。

这里是一个非答案:使用cdecl(或c + + decl)弄清楚:

 eisbaw@leno:~$ cdecl explain 'int (*(*foo)(const void *))[3]' declare foo as pointer to function (pointer to const void) returning pointer to array 3 of int 

它们为代码添加了额外的维度,而不会对语法进行重大改变。 想想这个:

 int a; a = 5 

只有一件事要改变: a 。 你可以写a = 6 ,结果对大多数人来说是显而易见的。 但现在考虑:

 int *a; a = &some_int; 

有两件事在不同的时间是相关的: a ,指针的实际值,以及指针后面的值。 你可以改变a

 a = &some_other_int; 

…和some_int仍然在某处具有相同的价值。 但是你也可以改变它指向的东西:

 *a = 6; 

a = 6只有局部副作用,而*a = 6可能会影响其他地方的其他一些事物。 我的观点并不是说间接的概念本质上是棘手的,而是因为你可以用a或一个间接的事物来处理直接的本地事物,而a事情可能是混淆了人们的事情。

我已经用c ++编写了2年,然后转换为Java(5年),从不回头。 然而,当我最近不得不使用一些本地的东西时,我惊奇地发现我没有忘记任何关于指针的东西,我甚至觉得它们很容易使用。 这和我7年前刚刚尝试把握这个概念时所经历的形成了鲜明的对比。 那么,我想理解和喜欢是一个编程成熟的问题吗? 🙂

要么

指针就像骑自行车,一旦你弄清楚如何与他们合作,就不会忘记它。

总而言之,很难理解,整个指针的想法是非常有教育意义的,我相信每个程序员都应该理解它,不pipe他是否用一个带有指针的语言来编程。

Pointers are difficult because of the indirection.

Pointers are a way of dealing with the difference between a handle to an object and an object itself. (ok, not necessarily objects, but you know what I mean, as well as where my mind is)

At some point, you probably have to deal with the difference between the two. In modern, high-level language this becomes the distinction between copy-by-value and copy-by-reference. Either way, it is a concept that is often difficult for programmers to grasp.

However, as has been pointed out, the syntax for handling this problem in C is ugly, inconsistent, and confusing. Eventually, if you really attempt to understand it, a pointer will make sense. But when you start dealing with pointers to pointers, and so on ad nauseum, it gets really confusing for me as well as for other people.

Another important thing to remember about pointers is that they're dangerous. C is a master programmer's language. It assumes you know what the heck you're doing and thereby gives you the power to really mess things up. While some types of programs still need to be written in C, most programs do not, and if you have a language that provides a better abstraction for the difference between an object and its handle, then I suggest you use it.

Indeed, in many modern C++ applications, it is often the case that any required pointer arithmetic is encapsulated and abstracted. We don't want developers doing pointer arithmetic all over the place. We want a centralized, well tested API that does pointer arithmetic at the lowest level. Making changes to this code must be done with great care and extensive testing.

I think one reason C pointers are difficult is that they conflate several concepts which are not really equivalent; yet, because they are all implemented using pointers, people can have a hard time disentangling the concepts.

In C, pointers are used to, amoung other things:

  • Define recursive data structures

In C you'd define a linked list of integers like this:

 struct node { int value; struct node* next; } 

The pointer is only there because this is the only way to define a recursive data structure in C, when the concept really has nothing to do with such a low-level detail as memory addresses. Consider the following equivalent in Haskell, which doesn't require use of pointers:

 data List = List Int List | Null 

Pretty straightforward – a list is either empty, or formed from a value and the rest of the list.

  • Iterate over strings and arrays

Here's how you might apply a function foo to every character of a string in C:

 char *c; for (c = "hello, world!"; *c != '\0'; c++) { foo(c); } 

Despite also using a pointer as an iterator, this example has very little in common with the previous one. Creating an iterator that you can increment is a different concept from defining a recursive data structure. Neither concept is especially tied to the idea of a memory address.

  • Achieve polymorphism

Here is an actual function signature found in glib :

 typedef struct g_list GList; void g_list_foreach (GList *list, void (*func)(void *data, void *user_data), void* user_data); 

哇! That's quite a mouthful of void* 's. And it's all just to declare a function that iterates over a list that can contain any kind of thing, applying a function to each member. Compare it to how map is declared in Haskell:

 map::(a->b)->[a]->[b] 

That's much more straightforward: map is a function that takes a function which converts an a to a b , and applies it to a list of a 's to yield a list of b 's. Just like in the C function g_list_foreach , map doesn't need to know anything in its own definition about the types to which it will be applied.

总结一下:

I think C pointers would be a lot less confusing if people first learned about recursive data structures, iterators, polymorphism, etc. as separate concepts, and then learned how pointers can be used to implement those ideas in C , rather than mashing all of these concepts together into a single subject of "pointers".

I think it requires a solid foundation, probably from the machine level, with introduction to some machine code, assembly, and how to represent items and data structure in RAM. It takes a little time, some homework or problem solving practice, and some thinking.

But if a person knows high level languages at first (which is nothing wrong — a carpenter uses an ax. a person who needs to split atom uses something else. we need people who are carpenters, and we have people who study atoms) and this person who knows high level language is given a 2 minute introduction to pointers, and then it is hard to expect him to understand pointer arithmetics, pointers to pointers, array of pointers to variable size strings, and array of array of characters, etc. A low-level solid foundation can help a lot.

The problem I have always had (primarily self-taught) is the "when" to use a pointer. I can wrap my head around the syntax for constructing a pointer but I need to know under which circumstances a pointer should be used.

Am I the only one with this mindset? 😉

Pointers (along with some other aspects of low-level work), require the user to take away the magic.

Most high level programmers like the magic.

Once upon a time… We had 8 bit microprocessors and everyone wrote in assembly. Most processors included some type of indirect addressing used for jump tables and kernels. When higher level languages came along we add a thin layer of abstraction and called them pointers. Over the years we have gotten more and more away from the hardware. This is not necessarily a bad thing. They are called higher level languages for a reason. The more I can concentrate on what I want to do instead of the details of how it is done the better.

It seems many students have a problem with the concept of indirection, especially when they meet the concept of indirection for the first time. I remember from back when I was a student that out of the +100 students of my course, only a handful of people really understood pointers.

The concept of indirection is not something that we often use in real life, and therefore it's a hard concept to grasp initially.

I have recently just had the pointer click moment, and I was surprised that I had been finding it confusing. It was more that everyone talked about it so much, that I assumed some dark magic was going on.

The way I got it was this. Imagine that all defined variables are given memory space at compile time(on the stack). If you want a program that could handle large data files such as audio or images, you wouldn't want a fixed amount of memory for these potential structures. So you wait until runtime to assign a certain amount of memory to holding this data(on the heap).

Once you have your data in memory, you don't want to be copying that data all around your memory bus every time you want to run an operation on it. Say you want to apply a filter to your image data. You have a pointer that starts at the front of the data you have assigned to the image, and a function runs across that data, changing it in place. If you didn't know what you we're doing, you would probably end up making duplicates of data, as you ran it through the operation.

At least that's the way I see it at the moment!

Speaking as a C++ newbie here:

The pointer system took a while for me to digest not necessarily because of the concept but because of the C++ syntax relative to Java. A few things I found confusing are:

(1) Variable declaration:

 A a(1); 

 A a = A(1); 

 A* a = new A(1); 

and apparently

 A a(); 

is a function declaration and not a variable declaration. In other languages, there's basically just one way to declare a variable.

(2) The ampersand is used in a few different ways. If it is

 int* i = &a; 

then the &a is a memory address.

OTOH, if it is

 void f(int &a) {} 

then the &a is a passed-by-reference parameter.

Although this may seem trivial, it can be confusing for new users – I came from Java and Java's a language with a more uniform use of operators

(3) Array-pointer relationship

One thing that's a tad bit frustrating to comprehend is that a pointer

 int* i 

can be a pointer to an int

 int *i = &n; // 

要么

can be an array to an int

 int* i = new int[5]; 

And then just to make things messier, pointers and array are not interchangeable in all cases and pointers cannot be passed as array parameters.

This sums up some of the basic frustrations I had with C/C++ and its pointers, which IMO, is greatly compounded by the fact that C/C++ has all these language-specific quirks.

I personally did not understand the pointer even after my post graduation and after my first job. The only thing I was knowing is that you need it for linked list, binary trees and for passing arrays into functions. This was the situation even at my first job. Only when I started to give interviews, I understand that the pointer concept is deep and has tremendous use and potential. Then I started reading K & R and writing own test program. My whole goal was job-driven.
At this time I found that pointers are really not bad nor difficult if they are been taught in a good way. Unfortunately when I learn C in graduation, out teacher was not aware of pointer, and even the assignments were using less of pointers. In the graduate level the use of pointer is really only upto creating binary trees and linked list. This thinking that you don't need proper understanding of pointers to work with them, kill the idea of learning them.

Pointers.. hah.. all about pointer in my head is that it give a memory address where the actual values of whatever its reference.. so no magic about it.. if you learn some assembly you wouldn't have that much trouble learning how pointers works.. come on guys… even in Java everything is a reference..

The main problem people do not understand why do they need pointers. Because they are not clear about stack and heap. It is good to start from 16bit assembler for x86 with tiny memory mode. It helped many people to get idea of stack, heap and "address". And byte:) Modern programmers sometimes can't tell you how many bytes you need to address 32 bit space. How can they get idea of pointers?

Second moment is notation: you declare pointer as *, you get address as & and this is not easy to understand for some people.

And the last thing I saw was storage problem: they understand heap and stack but can't get into idea of "static".