知识点9:动态内存分配(dynamic memory allocation)

Dynamic memory allocation sort of allows us the way
to get around this particular problem:

We might have an array that might be able to hold a lot of information, but it's still not exactly precise enough.

What if we don't know, what if we have no idea how much we'll need at compile time? Or what if our program will run for a really long time, accepting various user data, and we can't really estimate whether we're going to need 1,000 units? It's not like we can say at the command line enter how many items you think you'll need. Well what if that guess is wrong?

And the way it does it is by using pointers.

We can use pointers to get access to dynamically allocated memory, memory that is allocated as your program is running. It's not allocated at compile time.


When you dynamically allocate memory it comes from a pool of memory known as the heap.

Previously all the memory we've been working with in the course has been coming from a pool of memory known as the stack.

知识点9:动态内存分配(dynamic memory allocation)_第1张图片

A good way to generally keep in mind is that any time you give a variable a name, it probably lives on the stack.

And any time you don't give a variable a name, which you can do with dynamic memory allocation, it lives on the heap.

Now I'm kind of presenting this as if there's these two pools of memory.

知识点9:动态内存分配(dynamic memory allocation)_第2张图片

this diagram is is generally a representation of what memory looks like, and we're not going to care about all the stuff at the top and the bottom.

What we care about is this part in the middle here, heap and stack.

知识点9:动态内存分配(dynamic memory allocation)_第3张图片

As you can see by looking at this diagram, these actually aren't two separate pools of memory.

It's one shared pool of memory where you start, in this visual you start at the bottom and start filling up from the bottom with the stack, and you start at the top and start filling up from the top down with the heap.

But it really is the same pool, it's just different spots, different locations in memory that are being allocated.

And you can run out of memory by either having the heap go all the way to the bottom, or have the stack go all the way to the top, or having the heap and the stack meet up against each other.

All of those can be conditions that cause your program to run out of memory.


So how do we get dynamically allocated memory in the first place? How does our program get memory as it's running?

Well C provides a function called malloc, memory allocator, which you make a call to, and you pass in how many bytes of memory that you want.

So if your program is running and you want an integer runtime, you might mallock four bytes of memory, malloc parentheses four.

知识点9:动态内存分配(dynamic memory allocation)_第4张图片

mallock will go through looking through the heap, because we're dynamically allocating memory, and it will return to you a pointer to that memory.

It doesn't give you that memory -- it doesn't give it a name, it gives you a pointer to it.

If mallock can't give you any memory because you've run out, it'll give you back a null pointer. We suffer a seg fault.

So every time you make a call to malloc you always, always need to check whether or not the pointer it gave you back is null.

If it is, you need to end your program because if you try and dereference the null pointer you're going to suffer a segmentation fault and your program is going to crash anyway.


So how do we statically obtain an integer?

知识点9:动态内存分配(dynamic memory allocation)_第5张图片

int x.

We've probably done that a bunch of times, right?

This creates a variable called x that lives on the stack.

知识点9:动态内存分配(dynamic memory allocation)_第6张图片

How do we dynamically obtain an integer?

Int star px equals malloc 4.

Or more appropriately we'd say int star px equals malloc size of int, just to throw some fewer magic numbers around our program.

知识点9:动态内存分配(dynamic memory allocation)_第7张图片

This is going to obtain for us four bytes of memory from the heap, and the pointer we get back to it is called px. And then just as we've done previously we can dereference px to access that memory.

What if we want to create an array of x floats that live on the stack?

float stack_array -- that's the name of our array -- square brackets x. That will create for us an array of x floats that live on the stack.

We can create an array of floats that lives on the heap, too.

知识点9:动态内存分配(dynamic memory allocation)_第8张图片

The syntax might look a little more cumbersome, but we can say float star heap_array equals malloc x times the size of the float. I need enough room to hold x floating point values.

So say I need 100 floats, or 1,000 floats. So in that case it would be 400 bytes for 100 floats, or 4,000 bytes for 1,000 floats, because each float takes up four bytes of space.

After doing this I can use the square bracket syntax on heap_array.

Just as I would on stack_array, I can access its elements individually using heap_array zero, heap_array one. But recall the reason we can do that is because the name of an array in C is really a pointer to that array's first element.

So the fact that we're declaring an array of floats on the stack here is actually a bit misleading. We really are in the second line of code there also creating a pointer to a chunk of memory that we then do some work with.


Here's the big problem with dynamically allocated memory though, and this is why it's really important to develop some good habits when you're working with it.

Unlike statically declared memory, your memory is not automatically returned to the system when your function is done.

So if we have main, and main calls a function f, when f finishes whatever it's doing and returns control of the program back to main, all of the memory that f used is given back. It can be used again by some other program, or some other function that gets called later on in main. It can use that same memory over again.

If you dynamically allocate memory though you have to explicitly tell the system that you're done with it. It'll hold onto it for you, which could lead to a problem of you running out of memory.

And in fact we sometimes refer to this as a memory leak.

And sometimes these memory leaks can actually be really devastating for system performance.

知识点9:动态内存分配(dynamic memory allocation)_第9张图片

How do we give memory back when we're done with it? Well fortunately it's a very easy way to do it. We just free it.

There's a function called free, it accepts a pointer to memory,

So let's say we're in the middle of our program, we want to malloc 50 characters.

We want to malloc an array that can capable of holding 50 characters. And when we get a pointer back to that, that pointer's name is word. We do whatever we're going to do with word, and then when we're done we just free it. And now we have returned those 50 bytes of memory back to the system.

知识点9:动态内存分配(dynamic memory allocation)_第10张图片

So there are three golden rules that should be kept in mind whenever you're dynamically allocating memory with malloc.

知识点9:动态内存分配(dynamic memory allocation)_第11张图片

So let's go through an example here of what some dynamically allocated memory might look like mixed in with some static memory.

So we say int m.

知识点9:动态内存分配(dynamic memory allocation)_第12张图片

What if I then say int star a?

知识点9:动态内存分配(dynamic memory allocation)_第13张图片

So I'm coloring it green-ish as well. I know it has something to do with an integer, but it's not itself an integer.

But it's pretty much the same idea. I've created a box. Both of these right now live on the stack. I've given them both names.

Then, int star b equals malloc size of int.

知识点9:动态内存分配(dynamic memory allocation)_第14张图片

Well this doesn't just create one box. This actually creates two boxes. And it ties(连接在一起), it also establishes a point in a relationship.

We've allocated one block of memory on the heap. Notice that the top right box there does not have a name.

We mallocd it. It exists on the heap. But b has a name. It's a pointer variable called b. That lives on the stack.

So it's a piece of memory that points to another one. b contains the address of that block of memory. It doesn't have a name otherwise. But it points to it.

Now we'll get little more straightforward again. a equals ampersand m.

Do you recall what a equals ampersand m is? Well that's a gets m's address. Or put more diagrammatically, a points to m.

知识点9:动态内存分配(dynamic memory allocation)_第15张图片

OK so here's another one.

A equals b.

What's going to happen to the diagram this time?

Well recall that the assignment operator works by assigning the value on the right to the value on the left.

So instead of a pointing to m, a now points to the same place that b points. a doesn't point to b, a points where b points.

知识点9:动态内存分配(dynamic memory allocation)_第16张图片

If a pointed to b that would have been a equals ampersand(&) b.

But instead a equals b just means that a and b are now pointing to the same address, because inside of b is just an address. And now inside of a is the same address.

m equals 10, probably the most straightforward thing we've done in a little bit. Put the 10 in the box.

知识点9:动态内存分配(dynamic memory allocation)_第17张图片

Star b equals m plus 2, recall from our pointers video what star b means.

We're going to dereference b and put some value in that memory location. In this case 12.

知识点9:动态内存分配(dynamic memory allocation)_第18张图片

So when we dereference a point of recall we just travel down the arrow(遵循着箭头的轨迹).

Or put another way, we go to that memory address and we manipulate it in some way. We put some value in there.

In this case star b equals m plus 2 is just go to the variable pointed to by b, go to the memory pointed to by b, and put m plus 2 in there, 12.

Now I free b.

What happens when I free b?

I'm done working with it, right? I essentially give up the memory. I give it back to the system. I don't need this anymore is what I'm telling them, OK?

知识点9:动态内存分配(dynamic memory allocation)_第19张图片

Now if I say star a equals 11 you can probably already tell that something bad is going to happen here, right? And indeed if I tried that I probably would suffer a segmentation fault.

知识点9:动态内存分配(dynamic memory allocation)_第20张图片
suffer a segmentation fault

Because now, although previously that chunk of memory was something that I had access to, at this point now I'm accessing memory that is not legal for me to access.

And as we will probably recall, when we access memory that we're not supposed to touch, that's the most common cause of a segmentation fault. And so my program would crash if I tried to do this.


结语:

So again it's a good idea to get good practice and good habits ingrained when working with malloc and free, so that you don't suffer segmentation faults, and that you use your dynamically allocated memory responsibly.

你可能感兴趣的:(知识点9:动态内存分配(dynamic memory allocation))