理解指针有什么障碍,克服它们可以做些什么?

c++ c pointers

72787 观看

28回复

4114 作者的声誉

对于C或C ++中的许多新的,甚至是老的大学生来说,为什么指针会成为混乱的主要因素?是否有任何工具或思维过程可以帮助您理解指针在变量,函数和更高级别的工作方式?

有什么好的做法可以让人达到“啊哈,我明白了”的水平,而不会让他们陷入整体概念的困境?基本上,钻取方案。

作者: David McGraw 的来源 发布者: 2008 年 8 月 8 日

回应 28


27

6567 作者的声誉

Why are pointers such a leading factor of confusion for many new, and even old, college level students in the C/C++ language?

The concept of a placeholder for a value - variables - maps onto something we're taught in school - algebra. There isn't an existing parallel you can draw without understanding how memory is physically laid out within a computer, and no one thinks about this kind of thing until they're dealing with low level things - at the C/C++/byte communications level.

Are there any tools or thought processes that helped you understand how pointers work at the variable, function, and beyond level?

Addresses boxes. I remember when I was learning to program BASIC into microcomputers, there were these pretty books with games in them, and sometimes you had to poke values into particular addresses. They had a picture of a bunch of boxes, incrementally labelled with 0, 1, 2... and it was explained that only one small thing (a byte) could fit in these boxes, and there were a lot of them - some computers had as many as 65535! They were next to each other, and they all had an address.

What are some good practice things that can be done to bring somebody to the level of, "Ah-hah, I got it," without getting them bogged down in the overall concept? Basically, drill like scenarios.

For a drill? Make a struct:

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;

Same example as above, except in 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);

Output:

Start: my_pointer = s
After: my_pointer = t
Then: my_pointer = r
End: my_pointer = u

Perhaps that explains some of the basics through example?

作者: Josh 发布者: 2008 年 8 月 8 日

9

25027 作者的声誉

我不认为指针作为一个概念特别棘手 - 大多数学生的心理模型都映射到这样的东西,一些快速的盒子草图可以帮助。

困难,至少是我过去经历过和其他人看到过的难点,就是C / C ++中指针的管理可能会不经意地复杂化。

作者: Matt Mitchell 发布者: 2008 年 8 月 8 日

0

11524 作者的声誉

我没有看到关于指针的混乱。它们指向内存中的某个位置,即存储内存地址。在C / C ++中,您可以指定指针指向的类型。例如:

int* my_int_pointer;

说my_int_pointer包含一个包含int的位置的地址。

指针的问题在于它们指向内存中的某个位置,因此很容易跟踪到您不应该进入的某个位置。作为证据,请查看缓冲区溢出中C / C ++应用程序中的众多安全漏洞(递增指针)超过分配的边界)。

作者: grom 发布者: 2008 年 8 月 8 日

48

817 作者的声誉

The reason pointers seem to confuse so many people is that they mostly come with little or no background in computer architecture. Since many don't seem to have an idea of how computers (the machine) is actually implemented - working in C/C++ seems alien.

A drill is to ask them to implement a simple bytecode based virtual machine (in any language they chose, python works great for this) with an instruction set focussed on pointer operations (load, store, direct/indirect addressing). Then ask them to write simple programs for that instruction set.

Anything requiring slightly more than simple addition is going to involve pointers and they are sure to get it.

作者: JSN 发布者: 2008 年 8 月 8 日

3

24601 作者的声誉

我认为人们遇到麻烦的主要原因是因为它通常不是以有趣和引人入胜的方式教授的。我希望看到一位讲师从人群中获得10名志愿者并给他们一个1米的标尺,让他们以一定的配置站立并使用统治者指向对方。然后通过移动人们(以及他们指向统治者的地方)来显示指针算术。它是一种简单但有效(并且最重要的是令人难忘)的方式来展示概念,而不会在机制中陷入困境。

Once you get to C and C++ it seems to get harder for some people. I'm not sure if this is because they are finally putting theory that they don't properly grasp into practice or because pointer manipulation is inherently harder in those languages. I can't remember my own transition that well, but I knew pointers in Pascal and then moved to C and got totally lost.

作者: Mike Minutillo 发布者: 2008 年 8 月 8 日

742

301457 作者的声誉

决定

指针是一个概念,许多人一开始可能会感到困惑,特别是在复制指针值并仍然引用相同的内存块时。

我发现最好的比喻是将指针视为一张纸上有一个房屋地址,以及它作为实际房屋引用的内存块。因此可以容易地解释各种操作。

我在下面添加了一些Delphi代码,并在适当的地方添加了一些注释。我选择了Delphi,因为我的其他主要编程语言C#没有以同样的方式展示内存泄漏等内容。

如果您只想学习指针的高级概念,那么您应该忽略下面解释中标记为“Memory layout”的部分。它们旨在提供操作后内存可能看起来像的示例,但它们本质上更低级。但是,为了准确地解释缓冲区溢出是如何工作的,重要的是我添加了这些图表。

免责声明:出于所有意图和目的,这种解释和示例存储器布局大大简化。如果您需要在低级别处理内存,则需要了解更多开销和更多详细信息。但是,对于解释内存和指针的意图,它足够准确。


让我们假设下面使用的THouse类看起来像这样:

type
    THouse = class
    private
        FName : array[0..9] of Char;
    public
        constructor Create(name: PChar);
    end;

初始化house对象时,将为构造函数指定的名称复制到私有字段FName中。有一个原因,它被定义为固定大小的数组。

在内存中,会有一些与房屋分配相关的开销,我将在下面说明如下:

--- [ttttNNNNNNNNNN] ---
     ^ ^
     | |
     | +  -  FName数组
     |
     +  - 开销

“tttt”区域是开销,对于各种类型的运行时和语言,通常会有更多这样的内容,例如8或12字节。必须确保在此区域中存储的任何值都不会被内存分配器或核心系统例程以外的任何内容更改,否则您可能会崩溃程序。


分配内存

让一个企业家建造你的房子,并给你房子的地址。与现实世界相比,内存分配无法分配到哪里,但会找到一个有足够空间的合适位置,并将地址报告给分配的内存。

换句话说,企业家会选择现场。

THouse.Create('My house');

内存布局:

--- [ttttNNNNNNNNNN] ---
    1234我的房子

使用地址保留变量

在一张纸上写下你的新房子的地址。本文将作为您对您家的参考。没有这张纸,你就会迷路,找不到房子,除非你已经在里面。

var
    h: THouse;
begin
    h := THouse.Create('My house');
    ...

内存布局:

    H
    v
--- [ttttNNNNNNNNNN] ---
    1234我的房子

复制指针值

只需在新纸上写下地址即可。你现在有两张纸可以带你到同一栋房子,而不是两栋独立的房子。任何试图从一篇论文中删除地址并重新安排在那所房子里的家具的尝试都会使另一栋房子看起来像是以同样的方式被修改,除非你能明确地发现它实际上只是一栋房子。

注意这通常是我向人们解释最多问题的概念,两个指针并不意味着两个对象或内存块。

var
    h1, h2: THouse;
begin
    h1 := THouse.Create('My house');
    h2 := h1; // copies the address, not the house
    ...
    H1
    v
--- [ttttNNNNNNNNNN] ---
    1234我的房子
    ^
    H2

释放记忆

拆毁房子。如果您愿意,您可以稍后重新使用纸张换新地址,或者清除它以忘记不再存在的房屋地址。

var
    h: THouse;
begin
    h := THouse.Create('My house');
    ...
    h.Free;
    h := nil;

在这里,我首先建造房子,并掌握它的地址。然后我对房子做了一些事情(使用它,...代码,留给读者练习),然后我释放它。最后我清除了变量中的地址。

内存布局:

    h < -  +
    v +  - 免费之前
--- [ttttNNNNNNNNNN] --- |
    1234我的房子< -  +

    h(现在无处可寻)< -  +
                                +  - 免费之后
---------------------- | (注意,记忆可能仍然存在
    xx34我的房子< -  +包含一些数据)

晃来晃去的指针

你告诉你的企业家摧毁房子,但你忘了从你的纸上擦掉地址。当你以后看到那张纸时,你已经忘记了房子已经不在了,然后去看望它,结果失败了(另见下面关于无效参考的部分)。

var
    h: THouse;
begin
    h := THouse.Create('My house');
    ...
    h.Free;
    ... // forgot to clear h here
    h.OpenFrontDoor; // will most likely fail

h通话后使用.Free 可能会起作用,但那只是纯粹的运气。最有可能的是,它会在客户所在地,在关键操作过程中失败。

    h < -  +
    v +  - 免费之前
--- [ttttNNNNNNNNNN] --- |
    1234我的房子< -  +

    h < -  +
    v +  - 免费之后
---------------------- |
    xx34我的房子< -  +

正如您所看到的,h仍然指向内存中数据的残余,但由于它可能不完整,因此使用它可能会失败。


内存泄漏

你失去了那张纸,找不到房子。房子仍然站在某个地方,当你以后想要建造一个新房子时,你不能重复使用那个地方。

var
    h: THouse;
begin
    h := THouse.Create('My house');
    h := THouse.Create('My house'); // uh-oh, what happened to our first house?
    ...
    h.Free;
    h := nil;

在这里,我们h用一个新房子的地址覆盖了变量的内容,但旧的房子仍然站在......某个地方。在这段代码之后,没有办法到达那个房子,它将保持不变。换句话说,分配的内存将保持分配,直到应用程序关闭,此时操作系统将拆除它。

首次分配后的内存布局:

    H
    v
--- [ttttNNNNNNNNNN] ---
    1234我的房子

第二次分配后的内存布局:

                       H
                       v
--- [ttttNNNNNNNNNN] --- [ttttNNNNNNNNNN]
    1234我的房子5678我的房子

获得此方法的一种更常见的方法就是忘记释放某些内容,而不是像上面那样覆盖它。在Delphi术语中,这将通过以下方法发生:

procedure OpenTheFrontDoorOfANewHouse;
var
    h: THouse;
begin
    h := THouse.Create('My house');
    h.OpenFrontDoor;
    // uh-oh, no .Free here, where does the address go?
end;

执行此方法后,我们的变量中没有地方存在房子的地址,但房子仍在那里。

内存布局:

    h < -  +
    v +  - 丢失指针之前
--- [ttttNNNNNNNNNN] --- |
    1234我的房子< -  +

    h(现在无处可寻)< -  +
                                +  - 丢失指针后
--- [ttttNNNNNNNNNN] --- |
    1234我的房子< -  +

如您所见,旧数据在内存中保持不变,内存分配器不会重用。分配器会跟踪已使用的内存区域,除非您释放它,否则不会重复使用它们。


释放内存但保留(现在无效)引用

拆毁房子,擦掉其中一张纸,但你还有另一张纸上有旧地址,当你去地址时,你找不到房子,但你可能会发现类似废墟的东西一个。

也许你甚至会找到一所房子,但它不是你最初被给予地址的房子,因此任何试图使用它的方式就像它属于你一样可能会失败。

有时你甚至可能会发现邻近的地址上有一个相当大的房子,占据了三个地址(主街1-3),你的地址就到了房子的中间。将大型3地址房屋的那部分视为单个小房子的任何尝试都可能会失败。

var
    h1, h2: THouse;
begin
    h1 := THouse.Create('My house');
    h2 := h1; // copies the address, not the house
    ...
    h1.Free;
    h1 := nil;
    h2.OpenFrontDoor; // uh-oh, what happened to our house?

在这里,房子被拆除,通过参考h1,同时h1被清除,h2仍然有旧的,过时的地址。进入不再站立的房屋可能会或可能不会。

这是上面悬空指针的变体。查看其内存布局。


缓冲区溢出

你把更多东西搬进房子里,而不是你可能适合的东西,溢出到邻居的房子或院子里。当邻近房子的主人回家后,他会找到他认为属于他自己的各种东西。

这就是我选择固定大小数组的原因。要设置阶段,假设我们分配的第二个房子由于某种原因将被放置在内存中的第一个房子之前。换句话说,第二个房子的地址低于第一个房子的地址。而且,它们彼此相邻分配。

因此,这段代码:

var
    h1, h2: THouse;
begin
    h1 := THouse.Create('My house');
    h2 := THouse.Create('My other house somewhere');
                         ^-----------------------^
                          longer than 10 characters
                         0123456789 <-- 10 characters

首次分配后的内存布局:

                        H1
                        v
----------------------- [ttttNNNNNNNNNN]
                        5678我的房子

第二次分配后的内存布局:

    h2 h1
    VV
--- [ttttNNNNNNNNNN] ---- [ttttNNNNNNNNNN]
    1234My other house somewhereouse
                        ^---+--^
                            |
                            +- overwritten

The part that will most often cause crash is when you overwrite important parts of the data you stored that really should not be randomly changed. For instance it might not be a problem that parts of the name of the h1-house was changed, in terms of crashing the program, but overwriting the overhead of the object will most likely crash when you try to use the broken object, as will overwriting links that is stored to other objects in the object.


Linked lists

When you follow an address on a piece of paper, you get to a house, and at that house there is another piece of paper with a new address on it, for the next house in the chain, and so on.

var
    h1, h2: THouse;
begin
    h1 := THouse.Create('Home');
    h2 := THouse.Create('Cabin');
    h1.NextHouse := h2;

在这里,我们创建了从我们家到我们的小屋的链接。我们可以跟随链条直到房子没有NextHouse参考,这意味着它是最后一个。要访问我们所有的房屋,我们可以使用以下代码:

var
    h1, h2: THouse;
    h: THouse;
begin
    h1 := THouse.Create('Home');
    h2 := THouse.Create('Cabin');
    h1.NextHouse := h2;
    ...
    h := h1;
    while h <> nil do
    begin
        h.LockAllDoors;
        h.CloseAllWindows;
        h := h.NextHouse;
    end;

内存布局(将NextHouse添加为对象中的链接,使用下图中的四个LLLL注释):

    h1 h2
    VV
--- [ttttNNNNNNNNNNLLLL] ---- [ttttNNNNNNNNNNLLLL]
    1234Home + 5678Cabin +
                   | ^ |
                   + -------- + *(没有链接)

基本来说,什么是内存地址?

内存地址基本上只是一个数字。如果您将内存视为一个大字节数组,则第一个字节的地址为0,下一个字节的地址为1,依此类推。这是简化的,但足够好。

所以这个内存布局:

    h1 h2
    VV
--- [ttttNNNNNNNNNN] --- [ttttNNNNNNNNNN]
    1234我的房子5678我的房子

可能有这两个地址(最左边 - 是地址0):

  • h1 = 4
  • h2 = 23

这意味着我们上面的链表可能实际上是这样的:

    h1(= 4)h2(= 28)
    VV
--- [ttttNNNNNNNNNNLLLL] ---- [ttttNNNNNNNNNNLLLL]
    1234Home 0028 5678Cabin 0000
                   | ^ |
                   + -------- + *(没有链接)

通常将“无处点”的地址存储为零地址。


从基本的角度来说,什么是指针?

指针只是一个包含内存地址的变量。你通常可以要求编程语言给你它的编号,但是大多数编程语言和运行时都试图隐藏下面有一个数字的事实,因为数字本身对你没有任何意义。最好将指针视为黑盒子,即。只要它有效,你就不知道或关心它是如何实际实现的。

作者: Lasse Vågsæther Karlsen 发布者: 2008 年 8 月 8 日

9

8530 作者的声誉

具有一组良好图表的教程示例有助于理解指针

乔尔·斯波尔斯基(Joel Spolsky)在他的“ 游击队采访指南”中采用了一些关于理解指针的好点:

出于某种原因,大多数人似乎是在没有理解指针的大脑部分出生的。这是一种天赋,而不是技能 - 它需要一种复杂形式的双向间接思维,有些人根本无法做到。

作者: David 发布者: 2008 年 8 月 8 日

150

775 作者的声誉

在我的第一个Comp Sci课程中,我们进行了以下练习。当然,这是一个大约有200名学生的演讲厅......

教授在董事会上写道: int john;

约翰站起来

教授写道: int *sally = &john;

约翰站起来,指着约翰

教授: int *bill = sally;

比尔站起来,指着约翰

教授: int sam;

萨姆站起来

教授: bill = &sam;

比尔现在指向萨姆。

I think you get the idea. I think we spent about an hour doing this, until we went over the basics of pointer assignment.

作者: Tryke 发布者: 2008 年 8 月 8 日

12

40564 作者的声誉

The complexities of pointers go beyond what we can easily teach. Having students point to each other and using pieces of paper with house addresses are both great learning tools. They do a great job of introducing the basic concepts. Indeed, learning the basic concepts is vital to successfully using pointers. However, in production code, it's common to get into much more complex scenarios than these simple demonstrations can encapsulate.

I've been involved with systems where we had structures pointing to other structures pointing to other structures. Some of those structures also contained embedded structures (rather than pointers to additional structures). This is where pointers get really confusing. If you've got multiple levels of indirection, and you start ending up with code like this:

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

it can get confusing really quickly (imagine a lot more lines, and potentially more levels). Throw in arrays of pointers, and node to node pointers (trees, linked lists) and it gets worse still. I've seen some really good developers get lost once they started working on such systems, even developers who understood the basics really well.

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).

I think the best thing colleges can do to help students learn pointers is to to use good demonstrations, combined with projects that require pointer use. One difficult project will do more for pointer understanding than a thousand demonstrations. Demonstrations can get you a shallow understanding, but to deeply grasp pointers, you have to really use them.

作者: Derek Park 发布者: 2008 年 8 月 10 日

2

4783 作者的声誉

I don't think that pointers themselves are confusing. Most people can understand the concept. Now how many pointers can you think about or how many levels of indirection are you comfortable with. It doesn't take too many to put people over the edge. The fact that they can be changed accidently by bugs in your program can also make them very difficult to debug when things go wrong in your code.

作者: bruceatk 发布者: 2008 年 8 月 10 日

19

7068 作者的声誉

I found Ted Jensen's "Tutorial on Pointers and Arrays in C" an excellent resource for learning about pointers. It is divided into 10 lessons, beginning with an explanation of what pointers are (and what they're for) and finishing with function pointers. http://home.netcom.com/~tjensen/ptr/cpoint.htm

Moving on from there, Beej's Guide to Network Programming teaches the Unix sockets API, from which you can begin to do really fun things. http://beej.us/guide/bgnet/

作者: Ted Percival 发布者: 2008 年 8 月 11 日

4

1102 作者的声誉

我喜欢房子地址的比喻,但我一直都想到了邮箱本身的地址。这样,您可以可视化取消引用指针(打开邮箱)的概念。

例如,在链接列表之后:1)从您的纸张开始,带有地址2)转到纸张上的地址3)打开邮箱找到一张新纸,上面有下一个地址

在线性链接列表中,最后一个邮箱中没有任何内容(列表的末尾)。在循环链接列表中,最后一个邮箱具有其中第一个邮箱的地址。

请注意,步骤3是取消引用的地方,以及当地址无效时您将崩溃或出错的地方。假设你可以走到一个无效地址的邮箱,想象那里有一个黑洞或其他什么东西可以把世界彻底改变掉:)

作者: Christopher Scott 发布者: 2008 年 8 月 16 日

124

21106 作者的声誉

An analogy I've found helpful for explaining pointers is hyperlinks. Most people can understand that a link on a web page 'points' to another page on the internet, and if you can copy & paste that hyperlink then they will both point to the same original web page. If you go and edit that original page, then follow either of those links (pointers) you'll get that new updated page.

作者: Wilka 发布者: 2008 年 8 月 16 日

0

930 作者的声誉

只是为了混淆一些事情,有时你必须使用句柄而不是指针。句柄是指针的指针,因此后端可以在内存中移动内容以对堆进行碎片整理。如果指针在中间例程中发生变化,则结果是不可预测的,因此您首先必须锁定句柄以确保没有任何内容在任何地方。

http://arjay.bc.ca/Modula-2/Text/Ch15/Ch15.8.html#15.8.5谈论它比我更连贯一点。:-)

作者: SarekOfVulcan 发布者: 2008 年 8 月 22 日

5

4285 作者的声誉

我认为使得指针难以学习的是,直到指针你对“在这个内存位置是一组表示int,double,a character,what”的想法感到满意。

当你第一次看到指针时,你并没有真正得到那个内存位置的内容。“你是什么意思,它有一个地址?”

我不同意“你要么得到它们要么你没得到”这一概念。

当您开始为它们寻找实际用途时(例如不将大型结构传递到函数中),它们变得更容易理解。

作者: Baltimark 发布者: 2008 年 8 月 22 日

1

26747 作者的声誉

邮政信箱号码。

这是一条允许您访问其他内容的信息。

(如果你对邮局票号进行算术运算,你可能会遇到问题,因为这封信进入了错误的方框。如果有人移动到另一个状态 - 没有转发地址 - 那么你有一个悬空指针。另一方面 - 如果邮局转发邮件,那么你有一个指针指针。)

作者: joel.neely 发布者: 2008 年 8 月 29 日

7

12932 作者的声誉

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)doIt(mybuffer)doIt(*mybuffer)?)

一个数字,表示地址到缓冲区地址的地址

(也许那是doIt(&mybuffer)。或者是它doIt(&&mybuffer)?甚至是doIt(&&&mybuffer)

等等,所涉及的语言并没有明确表达,因为它涉及的词语“指针”和“引用”对我来说没有那么多含义和清晰度,因为“x将地址保持为y”和“这个函数需要一个地址y“。答案另外还取决于“mybuffer”的开头是什么,以及它打算用它做什么。该语言不支持实践中遇到的嵌套级别。就像当我必须将“指针”交给一个创建一个新缓冲区的函数时,它会修改指针以指向缓冲区的新位置。它确实需要指针或指针指针,因此它知道在哪里修改指针的内容。大多数时候我只需要猜测“是什么意思”

“指针”太过分了。指针是一个值的地址吗?或者它是一个保存值的地址的变量。当一个函数想要一个指针时,它是否需要指针变量所持有的地址,或者它是否希望地址指向变量?我糊涂了。

作者: Breton 发布者: 2009 年 4 月 16 日

2

15556 作者的声誉

I think it might actually be a syntax issue. The C/C++ syntax for pointers seems inconsistent and more complex than it needs to be.

Ironically, the thing that actually helped me to understand pointers was encountering the concept of an iterator in the c++ Standard Template Library. It's ironic because I can only assume that iterators were conceived as a generalization of the pointer.

Sometimes you just can't see the forest until you learn to ignore the trees.

作者: Waylon Flinn 发布者: 2009 年 4 月 17 日

1

1370 作者的声誉

通过迭代器抓住它并不是一个糟糕的方法..但是继续看你会看到Alexandrescu开始抱怨它们。

许多ex-C ++开发人员(在转储语言之前从未理解迭代器是一个现代指针)跳转到C#并且仍然认为他们有合适的迭代器。

嗯,问题在于所有迭代器都在运行时平台(Java / CLR)试图实现的完全赔率:新的,简单的,每个人都是开发者的用法。这可能是好的,但他们曾在紫皮书中说过,他们甚至在C之前和之前都说过:

间接。

一个非常强大的概念,但如果你一直这样做,从来没有这样做。迭代器很有用,因为它们有助于抽象算法,另一个例子。编译时是算法的地方,非常简单。你知道代码+数据,或者用其他语言C#:

IEnumerable + LINQ + Massive Framework = 300MB运行时惩罚间接糟糕,通过大量的引用类型实例拖动应用程序。

“Le Pointer很便宜。”

作者: rama-jka toti 发布者: 2009 年 4 月 17 日

5

0 作者的声誉

它难以理解的原因并不是因为它是一个困难的概念,而是因为语法不一致

   int *mypointer;

您首先了解到变量创建的最左侧部分定义了变量的类型。在C和C ++中,指针声明不起作用。相反,他们说变量指向左侧的类型。在这种情况下:*mypointer 指向一个int。

我没有完全掌握指针,直到我尝试在C#中使用它们(不安全),它们以完全相同的方式工作,但具有逻辑和一致的语法。指针本身就是一种类型。这里mypointer 一个指向int的指针。

  int* mypointer;

甚至不让我开始使用函数指针...

作者: burrrr 发布者: 2009 年 4 月 17 日

5

671 作者的声誉

当我只知道C ++时,我可以使用指针。我有点知道在某些情况下该做什么以及从试验/错误中无法做什么。但让我完全理解的是汇编语言。如果你使用你编写的汇编语言程序进行一些严格的指令级调试,你应该能够理解很多东西。

作者: toto 发布者: 2009 年 4 月 29 日

2

8657 作者的声誉

The confusion comes from the multiple abstraction layers mixed together in the "pointer" concept. Programmers don't get confused by ordinary references in Java/Python, but pointers are different in that they expose characteristics of the underlying memory-architecture.

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

作者: Joshua Fox 发布者: 2009 年 5 月 4 日

23

160686 作者的声誉

The reason I had a hard time understanding pointers, at first, is that many explanations include a lot of crap about passing by reference. All this does is confuse the issue. When you use a pointer parameter, you're still passing by value; but the value happens to be an address rather than, say, an int.

Someone else has already linked to this tutorial, but I can highlight the moment when I began to understand pointers:

A Tutorial on Pointers and Arrays in C: Chapter 3 - Pointers and Strings

int puts(const char *s);

For the moment, ignore the const. The parameter passed to puts() is a pointer, that is the value of a pointer (since all parameters in C are passed by value), and the value of a pointer is the address to which it points, or, simply, an address. Thus when we write puts(strA); as we have seen, we are passing the address of strA[0].

The moment I read these words, the clouds parted and a beam of sunlight enveloped me with pointer understanding.

Even if you're a VB .NET or C# developer (as I am) and never use unsafe code, it's still worth understanding how pointers work, or you won't understand how object references work. Then you'll have the common-but-mistaken notion that passing an object reference to a method copies the object.

作者: Ryan Lundy 发布者: 2010 年 1 月 13 日

8

212068 作者的声誉

我认为理解指针的主要障碍是坏老师。

几乎每个人都被教导谎言:它们只不过是内存地址,或者它们允许你指向任意位置

And of course that they are difficult to understand, dangerous and semi-magical.

None of which is true. Pointers are actually fairly simple concepts, as long as you stick to what the C++ language has to say about them and don't imbue them with attributes that "usually" turn out to work in practice, but nevertheless aren't guaranteed by the language, and so aren't part of the actual concept of a pointer.

I tried to write up an explanation of this a few months ago in this blog post -- hopefully it'll help someone.

(Note, before anyone gets pedantic on me, yes, the C++ standard does say that pointers represent memory addresses. But it does not say that "pointers are memory addresses, and nothing but memory addresses and may be used or thought of interchangeably with memory addresses". The distinction is important)

作者: jalf 发布者: 2010 年 1 月 13 日

0

123 作者的声誉

每个C / C ++初学者都有同样的问题,而且问题的发生不是因为“指针很难学”而是“谁和如何解释”。有些学习者在视觉上口头收集它,最好的解释方法是使用“训练”的例子(适用于口头和视觉的例子)。

“火车头”是一个指针不能嫌弃和“马车”是什么“火车头”拉动的尝试(或点)。之后,您可以对“旅行车”本身进行分类,它可以容纳动物,植物或人(或它们的混合物)。

作者: nurmurat 发布者: 2012 年 11 月 2 日

10

31147 作者的声誉

I thought I'd add an analogy to this list that I found very helpful when explaining pointers (back in the day) as a Computer Science Tutor; first, let's:


Set the stage:

Consider a parking lot with 3 spaces, these spaces are numbered:

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

In a way, this is like memory locations, they are sequential and contiguous.. sort of like an array. Right now there are no cars in them so it's like an empty array (parking_lot[3] = {0}).


Add the data

一个停车场永远不会长时间空着......如果这样做会毫无意义,没有人会建造任何停车场。所以,让我们说,当这一天的行动充满了3辆汽车,一辆蓝色汽车,一辆红色汽车和一辆绿色汽车:

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

这些车都是同一类型(轿车),所以想到这一点的一种方式是,我们的车有一些类型的数据(比如一个int),但它们有不同的值(blueredgreen,这可能是一种颜色enum


输入指针

现在,如果我带你进入这个停车场,并要求你找到一辆蓝色的汽车,你伸出一根手指并用它指向现场1的蓝色汽车。这就像拿一个指针并将它指定给一个记忆地址(int *finger = parking_lot

你的手指(指针)不是我的问题的答案。看你的手指会告诉我什么,但如果我期待在那里你手指指着(解引用指针),我能找到我一直在寻找汽车(数据)。


重新指定指针

现在我可以要求你找一辆红色轿车而你可以将你的手指重定向到一辆新车。现在你的指针(和以前一样)向我展示了相同类型(汽车)的新数据(可以找到红色汽车的停车位)。

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


双指针(或指针指针)

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

现在,如果我想知道蓝色汽车在哪里,我可以按照第一根手指的方向指向第二根手指,然后是汽车(数据)。


悬垂的指针

现在让我们说你感觉非常像一个雕像,你想要无限期地用手指着红色汽车。如果那辆红色车开走了怎么办?

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

你的指针仍然指向红色汽车的位置但不再指向。让我们说一辆新车在那里拉动......一辆橙色汽车。如果我再次问你,“红色汽车在哪里”,你仍指着那里,但现在你错了。那不是一辆红色的车,那是橙色的。


指针算术

好的,所以你仍然指着第二个停车位(现在被Orange车占用)

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

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

作者: Mike 发布者: 2013 年 4 月 5 日

1

872 作者的声誉

上面的一些答案断言“指针并不是很难”,但还没有直接解决“指针很难!”的问题。来自。几年前,我辅导了一年级的CS学生(仅仅一年,因为我很清楚),我很清楚指针的想法并不难。什么是难以理解为什么以及何时需要指针

我不认为你可以解释这个问题 - 为什么以及何时使用指针 - 来解释更广泛的软件工程问题。为什么每一个变量应该不会是一个全局变量,以及为什么要分解出类似的代码到功能(即,得到这个,使用指针专注自己的行为给他们的调用点)。

作者: Bernd Jendrissek 发布者: 2013 年 4 月 28 日

2

32198 作者的声誉

我喜欢解释它的方式是数组和索引 - 人们可能不熟悉指针,但他们通常知道索引是什么。

所以我想说RAM是一个数组(你只有10个字节的RAM):

unsigned char RAM[10] = { 10, 14, 4, 3, 2, 1, 20, 19, 50, 9 };

然后,指向变量的指针实际上只是RAM中变量(的第一个字节)的索引。

因此,如果你有一个指针/索引unsigned char index = 2,那么这个值显然是第三个元素,或者数字4.指向指针的指针是你获取该数字并将其用作索引本身的地方,如RAM[RAM[index]]

我会在纸张列表上绘制一个数组,并使用它来显示指向同一内存的许多指针,指针算术,指针指针等等。

作者: sashoalm 发布者: 2013 年 5 月 22 日
32x32