[MUSIC PLAYING] >> DOUG LLOYD: OK so a suggestion
before starting here. If you haven't watched the video on
pointers you might want to do so first. Because this video is another
way of working with pointers. >> So it's going to talk
about some concepts that we cover in the
pointers video, and we're going to gloss over them now,
assuming that they're already sort of understood. So that's just your fair warning
that if you're seeing this video and you haven't seen the
pointers video, it might sort of fly over your head a little bit. And so it might be better
to watch it in that order. >> So we have already seen one
way to work with pointers, which is we declare a
variable, and then we declare another variable, a pointer
variable, that points to it. So we've created a
variable with a name, we've created a second variable with a name,
and we point that second variable at that first. This sort of has a
problem though, because it requires us to know exactly
how much memory we're going to need the moment
our program is compiled. >> Why is that? Because we need to be able to name or
identify all of the possible variables we might encounter. 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? Dynamic memory allocation
sort of allows us the way to get around this particular problem. 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. A good way to generally
keep in mind-- and this rule doesn't always hold true,
but pretty much almost always holds true-- is that any
time you give a variable 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. But you may have seen this
diagram, which 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. 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 keep that in mind. When we talk about
the heap and the stack we are really talking about the
same general chunk of memory, just different portions of that 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. >> 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. And so that's why again I said
that it's important to maybe have watched the pointers video
before we get too far into this. So malloc's going to
give you back a pointer. >> If mallock can't give you any
memory because you've run out, it'll give you back a null pointer. Do you remember what happens if we
try and dereference a null pointer? We suffer a seg fault, right? That's probably not good. >> 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? >> int x. We've probably done that
a bunch of times, right? This creates a variable called
x that lives on the stack. 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. 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. How do we get an integer from the user? We can say int x equals get int. That's pretty straightforward. 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. 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. >> If you are a frequent internet user
you might use certain web browsers, and I won't name names here, but
there are some web browsers out there that are notorious for actually having
memory leaks that don't get fixed. And if you leave your browser open
for a very long period of time, days and days, or weeks, you sometimes
might notice that your system is running really, really slowly. And the reason for that is that
the browser has allocated memory, but then not told the system
that it's done with it. And so that leaves less memory
available for all of your other programs to have to share, because you're
leaking-- that web browser program is leaking memory. 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, and we're good to go. >> 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. Some other function can use them. We don't have to worry about suffering a
memory leak because we have freed word. We've given the memory back,
so we're done working with it. So there are three
golden rules that should be kept in mind whenever you're
dynamically allocating memory with malloc. Every block of memory that
you malloc must be freed before your program finishes running. Now again, in the appliance or in the
IDE this sort of happens for you anyway when you-- this will happen anyway
when your program is terminated, all the memory will be released. But it's generally good coding
practice to always, when you're done, free what you have mallocd. >> That said, only things that
you've mallocd should be freed. If you statically declare an
integer, int x semi-colon, that lives on the stack, you
don't then want to free x. So only things that you've
mallocd should be freed. >> And lastly, don't free something twice. That can lead to
another weird situation. So everything that you've
mallocd has to be freed. Only things that you've
malloc should be freed. And don't free something twice. >> So let's go through an example here
of what some dynamically allocated memory might look like mixed
in with some static memory. What might happen here? See if you can follow
along and guess what's going to happen as we go
through all these lines of code. >> So we say int m. What happens here? Well this is pretty straightforward. I create an integer variable called m. I color it green,
because that's the color that I use when I'm talking
about integer variables. It's a box. It's called m, and you can
store integers inside of it. >> What if I then say int star a? Well that's pretty similar. I'm creating a box called a. It's capable of holding int
stars, pointers to integers. 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. >> int star b equals malloc size of int. This one might be a little tricky. Take a second and think about what you
would expect to happen on this diagram. int star b equals malloc size of int. >> 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. So when we say int star b equals
malloc size of int, that right there, that arrow that popped up on the
right side there, that whole thing, I'll have it appear
again, is what happens. All of that happens in
that single line of code. 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. >> a equals b. 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. >> If a pointed to b that would
have been a equals ampersand b. But instead a equals b just
means that 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. 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. >> 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? Remember what I said free means. What am I saying 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? >> 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. 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. >> I'm Doug Lloyd this is CS50.