什么是理解指针和能够做些什么来克服这些障碍? [英] What are the barriers to understanding pointers and what can be done to overcome them?

查看:124
本文介绍了什么是理解指针和能够做些什么来克服这些障碍?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么指针许多新的,甚至古老的,大学级别C或C ++学生的困惑这样的主要因素?是否有任何工具或思考过程,帮助您了解指针在变量,函数是如何工作的,超越的水平?

Why are pointers such a leading factor of confusion for many new, and even old, college level students in C or C++? Are there any tools or thought processes that helped you understand how pointers work at the variable, function, and beyond level?

什么是可以做的,使某人的一级,一些好的做法事,啊,哈,我知道了,没有让他们在整体概念越陷越深?基本上,像钻场景。

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.

推荐答案

指针是一个概念,对于许多可以先混乱,特别是当它涉及到各地复制指针值仍然引用相同的内存块。

Pointers is a concept that for many can be confusing at first, in particular when it comes to copying pointer values around and still referencing the same memory block.

我发现最好的比喻是要考虑指针作为一张纸,上面有一所房子的地址,并引用作为实际房子的内存块。操作各种因而可以很容易解释。

I've found that the best analogy is to consider the pointer as a piece of paper with a house address on it, and the memory block it references as the actual house. All sorts of operations can thus be easily explained.

我添加了一些德尔福code楼下,有的意见适当。我选择了德尔福,因为我的其他主要编程语言,C#,不会出现同样的方式之类的东西内存泄漏。

I've added some Delphi code down below, and some comments where appropriate. I chose Delphi since my other main programming language, C#, does not exhibit things like memory leaks in the same way.

如果你只是想学习指针的高层次的概念,那么你应该忽略在下面的说明标有内存布局的部分。它们的目的是得到什么存储器可能看起来像操作后的例子,但它们在性质上更低的水平。然而,为了准确地解释如何缓冲区溢出真正的工作,但重要的是我添加这些图。

If you only wish to learn the high-level concept of pointers, then you should ignore the parts labelled "Memory layout" in the explanation below. They are intended to give examples of what memory could look like after operations, but they are more low-level in nature. However, in order to accurately explain how buffer overruns really work, it was important that I added these diagrams.

免责声明:对于所有的意图和目的,这样的解释和例子记忆
布局大大简化。还有更多的开销和大量的更多详细信息,你会
要知道,如果你需要处理内存在低水平的基础。然而,对于
解释内存和指针的意图,这是不够准确的。

假设下面使用的THouse类看起来是这样的:

Let's assume the THouse class used below looks like this:

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

在初始化房屋目标,考虑到构造函数中的名称复制到私有字段FName参数。有它被定义为固定大小的数组的一个原因。

When you initialize the house object, the name given to the constructor is copied into the private field FName. There is a reason it is defined as a fixed-size array.

在内存中,将有与房子相关的配置一些开销,下面我将说明这是这样的:

In memory, there will be some overhead associated with the house allocation, I'll illustrate this below like this:


---[ttttNNNNNNNNNN]---
     ^   ^
     |   |
     |   +- the FName array
     |
     +- overhead

的TTTT面积开销,通常会有更多的这种各类运行时和语言,如8个或12个字节的。当务之急是什么值存储在该区域将无法得到比内存分配器或核心系统程序以外的任何改变,或者你可能崩溃的程序。

The "tttt" area is overhead, there will typically be more of this for various types of runtimes and languages, like 8 or 12 bytes. It is imperative that whatever values are stored in this area never gets changed by anything other than the memory allocator or the core system routines, or you risk crashing the program.

分配内存

获得恩特雷里奥斯preneur来建房,并给你的地址的房子。与此相反的现实世界中,内存分配不能被告知在哪里分配,但会找到足够的空间,合适的地点,并汇报地址分配的内存。

Get an entrepreneur to build your house, and give you the address to the house. In contrast to the real world, memory allocation cannot be told where to allocate, but will find a suitable spot with enough room, and report back the address to the allocated memory.

在换句话说,恩特雷里奥斯preneur会选择的地方。

In other words, the entrepreneur will choose the spot.

THouse.Create('My house');

内存布​​局:


---[ttttNNNNNNNNNN]---
    1234My house


保持一个变量的地址

写在一张纸上的地址,你的新房子。本文将作为您的参考,以你的房子。如果没有这张纸,你就输了,而且找不到房子,除非你在它是了。

Write the address to your new house down on a piece of paper. This paper will serve as your reference to your house. Without this piece of paper, you're lost, and cannot find the house, unless you're already in it.

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

内存布​​局:


    h
    v
---[ttttNNNNNNNNNN]---
    1234My house


复制指针值

只要写上了一个新纸上的地址。你现在有两张纸,将让你在同一房子,而不是两个独立的房子。任何试图从一个纸按照地址,并重新安排在那个房子里的家具将使它看起来的其他房子的也以同样的方式被修改,除非你能明确地检测到它实际上只是一个房子

Just write the address on a new piece of paper. You now have two pieces of paper that will get you to the same house, not two separate houses. Any attempts to follow the address from one paper and rearrange the furniture at that house will make it seem that the other house has been modified in the same manner, unless you can explicitly detect that it's actually just one house.

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

Note This is usually the concept that I have the most problem explaining to people, two pointers does not mean two objects or memory blocks.

var
    h1, h2: THouse;
begin
    h1 := THouse.Create('My house');
    h2 := h1; // copies the address, not the house
    ...


    h1
    v
---[ttttNNNNNNNNNN]---
    1234My house
    ^
    h2


释放内存

拆除房子。然后,您可以在以后重新使用的纸张为一个新的地址,如果你愿意的话,或者清除它忘记地址已不存在的房子。

Demolish the house. You can then later on reuse the paper for a new address if you so wish, or clear it to forget the address to the house that no longer exists.

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

在这里,我第一次建造的房子,并获得其地址的搁置。然后我做一些事情来的房子(使用它了... code,作为练习留给读者),然后我释放它。最后,我从我的变量清除的地址。

Here I first construct the house, and get hold of its address. Then I do something to the house (use it, the ... code, left as an exercise for the reader), and then I free it. Lastly I clear the address from my variable.

内存布​​局:


    h                        <--+
    v                           +- before free
---[ttttNNNNNNNNNN]---          |
    1234My house             <--+

    h (now points nowhere)   <--+
                                +- after free
----------------------          | (note, memory might still
    xx34My house             <--+  contain some data)


悬摆指针

您告诉恩特雷里奥斯preneur摧毁的房子,但你忘了从纸上抹去的地址。如果以后你看一张纸,你忘了,房子已经不存在,并且去访问它,失败的结果(见下文关于无效引用的部分)。

You tell your entrepreneur to destroy the house, but you forget to erase the address from your piece of paper. When later on you look at the piece of paper, you've forgotten that the house is no longer there, and goes to visit it, with failed results (see also the part about an invalid reference below).

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

使用 ^ h 调用。免费之后的可能的工作,但是这是只是纯粹的运气。最有可能会失败,在一个客户的地方,在一个关键的操作的中间

Using h after the call to .Free might work, but that is just pure luck. Most likely it will fail, at a customers place, in the middle of a critical operation.


    h                        <--+
    v                           +- before free
---[ttttNNNNNNNNNN]---          |
    1234My house             <--+

    h                        <--+
    v                           +- after free
----------------------          |
    xx34My house             <--+

如你所见,H仍然指向内存中的数据的残余,但
因为它可能是不完整的,因为之前使用它可能会失败。

As you can see, h still points to the remnants of the data in memory, but since it might not be complete, using it as before might fail.

内存泄漏

您失去一张纸,找不到房子。房子依然屹立的地方,虽然,当你以后要建立一个新的房子,你不能重用现场。

You lose the piece of paper and cannot find the house. The house is still standing somewhere though, and when you later on want to construct a new house, you cannot reuse that spot.

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 变量,新房子的地址的内容,但旧的依然站在某处。这code后,就没有办法达到这个房子,它会被罚站。换句话说,分配的内存将保持分配,直到应用程序关闭,此时操作系统会撕裂下来。

Here we overwrote the contents of the h variable with the address of a new house, but the old one is still standing... somewhere. After this code, there is no way to reach that house, and it will be left standing. In other words, the allocated memory will stay allocated until the application closes, at which point the operating system will tear it down.

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

Memory layout after first allocation:


    h
    v
---[ttttNNNNNNNNNN]---
    1234My house

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

Memory layout after second allocation:


                       h
                       v
---[ttttNNNNNNNNNN]---[ttttNNNNNNNNNN]
    1234My house       5678My house

要获得这种方法更常见的方式就是忘记释放,而不是如上面覆盖它的东西。在Delphi而言,这将与以下的方法进行:

A more common way to get this method is just to forget to free something, instead of overwriting it as above. In Delphi terms, this will occur with the following method:

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

此方法执行后,有一个在我们的变量,该地址的房子不存在的地方,但房子仍然在那里。

After this method has executed, there's no place in our variables that the address to the house exists, but the house is still out there.

内存布​​局:


    h                        <--+
    v                           +- before losing pointer
---[ttttNNNNNNNNNN]---          |
    1234My house             <--+

    h (now points nowhere)   <--+
                                +- after losing pointer
---[ttttNNNNNNNNNN]---          |
    1234My house             <--+

正如你所看到的,旧的数据保留在内存中保存完好,不会
由存储器分配器被重用。分配器跟踪哪些
的存储器区域已经被使用,并且将不重复使用它们,除非你
释放。

As you can see, the old data is left intact in memory, and will not be reused by the memory allocator. The allocator keeps track of which areas of memory has been used, and will not reuse them unless you free it.

释放内存,但保持(现在是无效的)参考

拆除房子,擦除纸片的人,但你也有另外的一张纸,旧地址就可以了,当你去的地址,你不会找到一所房子,但你可能找到的东西,酷似一个废墟。

Demolish the house, erase one of the pieces of paper but you also have another piece of paper with the old address on it, when you go to the address, you won't find a house, but you might find something that resembles the ruins of one.

也许你会发现,即使房子,但它不是你最初提供的地址到房子,就好像它是属于你可能会失败,可怕因此任何企图使用它。

Perhaps you will even find a house, but it is not the house you were originally given the address to, and thus any attempts to use it as though it belongs to you might fail horribly.

有时候,你甚至可能会发现,相邻的地址有一个相当大的房子内就可以占据三个地址(主街1-3),你的地址去了房子的中间。任何试图把大3地址房子作为一个小房子的那部分可能还可怕失败。

Sometimes you might even find that a neighbouring address has a rather big house set up on it that occupies three address (Main Street 1-3), and your address goes to the middle of the house. Any attempts to treat that part of the large 3-address house as a single small house might also fail horribly.

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 仍然有旧的,超出过期,地址。访问房子不再站在可能会或可能无法正常工作。

Here the house was torn down, through the reference in h1, and while h1 was cleared as well, h2 still has the old, out-of-date, address. Access to the house that is no longer standing might or might not work.

这是上面的悬挂指针的变化。看到它的内存布局。

This is a variation of the dangling pointer above. See its memory layout.

缓冲区溢出

您移动更多的东西进了屋比你所能配合,蔓延到邻居的房子或院子。当邻居房子的主人后来回家时,他会发现所有的事情,他会考虑他自己的。

You move more stuff into the house than you can possibly fit, spilling into the neighbours house or yard. When the owner of that neighbouring house later on comes home, he'll find all sorts of things he'll consider his own.

这是我选择了一个固定大小的数组的原因。要设置舞台,假设
我们分配了第二个家将,出于某种原因,之前被放置
第一个在存储器中。换句话说,第二个房子将具有较低
地址比第一个。另外,他们就分配紧挨着对方。

This is the reason I chose a fixed-size array. To set the stage, assume that the second house we allocate will, for some reason, be placed before the first one in memory. In other words, the second house will have a lower address than the first one. Also, they're allocated right next to each other.

因此​​,该code:

Thus, this code:

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

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

Memory layout after first allocation:


                        h1
                        v
-----------------------[ttttNNNNNNNNNN]
                        5678My house

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

Memory layout after second allocation:


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

这是最常引起死机的部分是当你覆盖重要组成部分
数据的存储,你真的不应该随意变更。例如
它可能不是该H1的内部的名称的部分被改变的问题,
在崩溃的方案,但覆盖的开销方面
对象很可能会崩溃,当您尝试使用破碎对象,
如将重写存储到​​链接
在对象的其他对象。

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.

链表

当您按照在一张纸上的地址,你到了房子,那个房子还有另外一张纸,上面有一个新的地址,在链中的下家,等等。

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 引用,这意味着它是最后一个。要访问我们的所有房屋,我们可以使用下面的code:

Here we create a link from our home house to our cabin. We can follow the chain until a house has no NextHouse reference, which means it's the last one. To visit all our houses, we could use the following code:

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的按照下图):

Memory layout (added NextHouse as a link in the object, noted with the four LLLL's in the below diagram):


    h1                      h2
    v                       v
---[ttttNNNNNNNNNNLLLL]----[ttttNNNNNNNNNNLLLL]
    1234Home       +        5678Cabin      +
                   |        ^              |
                   +--------+              * (no link)


从本质上讲,什么是内存地址?

一个内存地址是基本条件只是一个数字。如果你觉得内存
作为一个字节大数组,第一个字节的地址为0,下一个
地址1等向上。这是简化,但不够好。

A memory address is in basic terms just a number. If you think of memory as a big array of bytes, the very first byte has the address 0, the next one the address 1 and so on upwards. This is simplified, but good enough.

所以这个内存布局:


    h1                 h2
    v                  v
---[ttttNNNNNNNNNN]---[ttttNNNNNNNNNN]
    1234My house       5678My house

可能有两个地址(最左边的 - 为地址为0):

Might have these two address (the leftmost - is address 0):


  • H1 = 4

  • H2 = 23

这意味着,我们的链表以上实际工作可能是这样的:

Which means that our linked list above might actuall look like this:


    h1 (=4)                 h2 (=28)
    v                       v
---[ttttNNNNNNNNNNLLLL]----[ttttNNNNNNNNNNLLLL]
    1234Home      0028      5678Cabin     0000
                   |        ^              |
                   +--------+              * (no link)

这是典型的存储地址的点无处作为一个零地址。

It is typical to store an address that "points nowhere" as a zero-address.

从本质上讲,什么是指针?

一个指针只是抱着一个内存地址的变量。您通常可以让编程
语言给你它的数目,但是大多数编程语言和运行时尝试
隐瞒事实,那就是下一个数字,只是因为数量本身并不
真正持有任何意义给你。最好是认为一个指针作为一个黑盒子,即。
你真的不知道也不关心它实际上是如何实现,只要你将它
工作原理。

A pointer is just a variable holding a memory address. You can typically ask the programming language to give you its number, but most programming languages and runtimes tries to hide the fact that there is a number beneath, just because the number itself does not really hold any meaning to you. It is best to think of a pointer as a black box, ie. you don't really know or care about how it is actually implemented, just as long as it works.

这篇关于什么是理解指针和能够做些什么来克服这些障碍?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆