72787 观看

28回复

4114 作者的声誉

### 回应 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?

9

25027 作者的声誉

0

11524 作者的声誉

int* my_int_pointer;

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.

3

24601 作者的声誉

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.

742

301457 作者的声誉

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

--- [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我的房子< -  +

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
v
--- [ttttNNNNNNNNNN] ---
1234我的房子

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

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我的房子< -  +

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?

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.

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;

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;

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

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

• h1 = 4
• h2 = 23

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

9

8530 作者的声誉

150

775 作者的声誉

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

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.

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.

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/

4

1102 作者的声誉

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.

0

930 作者的声誉

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

5

4285 作者的声誉

1

26747 作者的声誉

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

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.

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)

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

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.

1

1370 作者的声誉

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

“Le Pointer很便宜。”

5

0 作者的声誉

int *mypointer;

int* mypointer;

5

671 作者的声誉

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.

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.

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)

0

123 作者的声誉

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

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

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

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 |

1

872 作者的声誉

2

32198 作者的声誉

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