知识点8:指针(pointers)

What we really want to do here is train you to have good pointer discipline, so that you can use pointers effectively to make your programs that much better.

As I said pointers give us a different way to pass data between functions.

Now if you recall from an earlier video, when we were talking about variable scope, I mentioned that all the data that we pass between functions in C is passed by value.

And I may not have used that term, what I meant there was that we are passing copies of data. When we pass a variable to a function, we're not actually passing the variable to the function, right?

We're passing a copy of that data to the function. The function does what it will and it calculates some value, and maybe we use that value when it gives it back.


If we use pointers instead of using variables, or instead of using the variables themselves or copies of the variables, we can now pass the variables around between functions in a different way.


Before we dive into pointers, I think it's worth taking a few minutes to go back to basics here.

And have a look at how computer memory works because these two subjects are going to actually be pretty interrelated.


We work inside of RAM. And every time we're talking about memory, pretty much, in CS50, we're talking about RAM, not hard disk.

So when we move things into memory, it takes up a certain amount of space. All of the data types that we've been working with take up different amounts of space in RAM.

How many bytes of memory do strings take up? Well let's put a pin in that question for now, but we'll come back to it.


what is address?

Similarly every location in memory has an address.

So your memory might look something like this.

Here's a very small chunk of memory, this is 20 bytes of memory.

The first 20 bytes because my addresses there at the bottom are 0, 1, 2, 3, and so on all the way up to 19.

And when I declare variables and when I start to work with them, the system is going to set aside some space for me in this memory to work with my variables.

So I might say, char c equals capital H.

And what's going to happen? Well the system is going to set aside for me one byte.

In this case it chose byte number four, the byte at address four, and it's going to store the letter capital H in there for me.

If I then say int speed limit equals 65, it's going to set aside four bytes of memory for me.

And it's going to treat those four bytes as a single unit because what we're working with is an integer here. And it's going to store 65 in there.

Now already I'm kind of telling you a bit of a lie, right, because we know that computers work in binary. They don't understand necessarily what a capital H is or what a 65 is, they only understand binary, zeros and ones.

And so actually what we're storing in there is not the letter H and the number 65, but rather the binary representations thereof, which look a little something like this.

And in particular in the context of the integer variable, it's not going to just spit it into, it's not going to treat it as one four byte chunk necessarily, it's actually going to treat it as four one byte chunks, which might look something like this.

And even this isn't entirely true either, because of something called an endianness, which we're not going to get into now, but if you're curious about, you can read up on little and big endianness.

But let's just assume that is, in fact, how the number 65 would be represented in memory on every system, although it's not entirely true.


Now if I want to get another string, called surname, and I want to put Lloyd in there.

I'm going to need to fit one character, each letter of that's going to require one character, one byte of memory.

So if I could put Lloyd into my array like this I'm pretty good to go, right? What's missing?

Remember that every string we work with in C ends with backslash zero, and we can't omit that here, either.

We need to set aside one byte of memory to hold that so we know when our string has ended.


So again this arrangement of the way things appear in memory might be a little random, but it actually is how most systems are designed.

To line them up on multiples of four, for reasons again that we don't need to get into right now.

But this, so suffice it to say that after these three lines of code, this is what memory might look like. If I need memory locations 4, 8, and 12 to hold my data, this is what my memory might look like.

And just be particularly pedantic here, when we're talking about memory addresses we usually do so using hexadecimal notations.

So why don't we convert all of these from decimal to hexadecimal notation just because that's generally how we refer to memory.

So instead of being 0 through 19, what we have is zero x zero through zero x1 three.

Those are the 20 bytes of memory that we have or we're looking at in this image right here.


So all of that being said, let's step away from memory for a second and back to pointers.

Here is the most important thing to remember as we start working with pointers.

A pointer is just an address.

Pointers are addresses to locations in memory where variables live.


Another thing I like to do is to have sort of diagrams visually representing what's happening with various lines of code. And we'll do this a couple of times in pointers, and when we talk about dynamic memory allocation as well. Because I think that these diagrams can be particularly helpful.

之后的图示略


So what is a pointer?

A pointer is a data item whose value is a memory address.

That was that zero x eight zero stuff going on, that was a memory address. That was a location in memory.

And the type of a pointer describes the kind of data you'll find at that memory address.

So here's the really cool thing though. Pointers allow us to pass variables between functions. And actually pass variables and not pass copies of them. Because if we know exactly where in memory to find a variable, we don't need to make a copy of it, we can just go to that location and work with that variable.


So in essence pointers sort of make a computer environment a lot more like the real world, right.

So here's an analogy:

Let's say that I have a notebook, right, and it's full of notes. And I would like you to update it. You are a function that updates notes, right. In the way we've been working so far, what happens is you will take my notebook, you'll go to the copy store, you'll make a Xerox copy of every page of the notebook. You'll leave my notebook back on my desk when you're done, you'll go and cross out things in my notebook that are out of date or wrong, and then you'll pass back to me the stack of Xerox pages that is a replica of my notebook with the changes that you've made to it.

And at that point, it's up to me as the calling function, as the caller, to decide to take your notes and integrate them back into my notebook. So there's a lot of steps involved here, right. Like wouldn't it be better if I just say, hey, can you update my notebook for me, hand you my notebook, and you take things and literally cross them out and update my notes in my notebook. And then give me my notebook back. That's kind of what pointers allow us to do, they make this environment a lot more like how we operate in reality.


let's talk about how pointers work in C, and how we can start to work with them.

So there's a very simple pointer in C called the null pointer. The null pointer points to nothing.

if we don't set its value to something meaningful immediately, you should always set your pointer to point to null.

You should set it to point to nothing. That's very different than just leaving the value as it is and then declaring a pointer and just assuming it's null because that's rarely true.

So you should always set the value of a pointer to null if you don't set its value to something meaningful immediately.

You can check whether a pointer's value is null using the equality operator (==), just like you compare any integer values or character values using (==) as well. It's a special sort of constant value that you can use to test.


Another way to create a pointer is to extract the address of a variable you've already created, and you do this using the & operator address extraction.

arr square bracket i, if arr is an array of doubles, then arr square bracket i is the i-th element of that array, and &arr square bracket i is where in memory the i-th element of arr exists.

So what's the implication here?

An arrays name, the implication of this whole thing, is that an array's name is actually itself a pointer. You've been working with pointers all along every time that you've used an array. Remember from the example on variable scope, near the end of the video I present an example where we have a function called set int and a function called set array. And your challenge to determine whether or not, or what the values that we printed out the end of the function, at the end of the main program.

If you recall from that example or if you've watched the video, you know that when you- the call to set int effectively does nothing. But the call to set array does. And I sort of glossed over why that was the case at the time. I just said, well it's an array, it's special, you know, there's a reason.

The reason is that an array's name is really just a pointer, and there's this special square bracket syntax that make things a lot nicer to work with. And they make the idea of a pointer a lot less intimidating, and that's why they're sort of presented in that way. But really arrays are just pointers. And that's why when we made a change to the array, when we passed an array as a parameter to a function or as an argument to a function, the contents of the array actually changed in both the callee and in the caller. Which for every other kind of variable we saw was not the case.

So that's just something to keep in mind when you're working with pointers, is that the name of an array actually a pointer to the first element of that array.


OK so now we have all these facts, let's keep going, right.

Why do we care about where something lives.

Well like I said, it's pretty useful to know where something lives so you can go there and change it. Work with it and actually have the thing that you want to do to that variable take effect, and not take effect on some copy of it.

This is called dereferencing. We go to the reference and we change the value there. So if we have a pointer and it's called pc, and it points to a character, then we can say *pc and *pc is the name of what we'll find if we go to the address pc.

What we'll find there is a character and *pc is how we refer to the data at that location. So we could say something like *pc=D or something like that, and that means that whatever was at memory
address pc, whatever character was previously there, is now D, if we say *pc=D.


So here we go again with some weird C stuff, right.

So we've seen * previously as being somehow part of the data type, and now it's being used in a slightly different context to access the data at a location. I know it's a little confusing and that's actually part of this whole like, why pointers have this mythology around them as being so complex, is kind of a syntax problem, honestly. But * is used in both contexts, both as part of the type name, and we'll see a little later something else, too. And right now is the dereference operator. So it goes to the reference, it accesses the data at the location of the pointer, and allows you to manipulate it at will.

Now this is very similar to visiting your neighbor, right. If you know what your neighbor lives, you're not hanging out with your neighbor. You know you happen to know where they live, but that doesn't mean that by virtue of having that knowledge you are interacting with them. If you want to interact with them, you have to go to their house, you have to go to where they live. And once you do that, then you can interact with them just like you'd want to. And similarly with variables, you need to go to their address if you want to interact them, you can't just know the address. And the way you go to the address is to use *, the dereference operator.


What do you think happens if we try and dereference a pointer whose value is null?

Recall that the null pointer points to nothing. So if you try and dereference nothing or go to an address nothing, what do you think happens? Well if you guessed segmentation fault, you'd be right. If you try and dereference a null pointer, you suffer a segmentation fault.

But wait, didn't I tell you, that if you're not going to set your value of your pointer to something meaningful, you should set to null? I did and actually the segmentation fault is kind of a good behavior.


Tips:


OK so I kind of promised that we would revisit the concept of how large is a string.

Well string is really just an alias for something called the char *, a pointer to a character.

Well pointers, recall, are just addresses. So what is the size in bytes of a string? Well it's four or eight.

And the reason I say four or eight is because it actually depends on the system, If you're using CS50 ide, char * is the size of a char * is eight, it's a 64-bit system. Every address in memory is 64 bits long.

If you're using CS50 appliance or using any 32-bit machine, and you've heard that term 32-bit machine, what is a 32-bit machine? Well it just means that every address in memory is 32 bits long. And so 32 bits is four bytes(64 bits is eight bytes).

So a char * is four or eight bytes depending on your system.

And indeed any data types, and a pointer to any data type, since all pointers are just addresses, are four or eight bytes.

The End
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容