 That's what you get for showing up. All right, 20% of the class, we're still gonna go like, so you're either trying to decide are you all people who are done with project three, and so you're, I don't know what to call you, somebody who's done already, or you're a defeatist and decided you're never gonna manage, so you decided you might as well come to class and learn something. Or you're doing it right now, so you're a poor multi-tasker, and so you're not gonna be able to do project three or listen to the material, so you're just not doing either well. I don't know if that's a poor type of person. If you're like saving time now, like going to class, you don't have to watch this video later, like an 80% of the class will be doing. Yeah, there you go. Okay, or then over a cop, or just regular competent and not over-confident, because we'll find out tonight at midnight. Yeah, after this, you're up from three to midnight. What's that, like nine hours? A lot of hours, right? It's like a whole work day. All depends on how you think about it. All right, cool. Okay, so we're gonna go back to semantics, and we're gonna talk about pointer semantics, and unfortunately, if you're classmates, they're gonna have to learn all this stuff on their own, because this is important, super interesting, awesome pointer stuff that we're learning. And then from here, we're gonna go into other types of semantics. So we're gonna go into memory semantics, talking about independent memory, and everything like that. Are you gonna talk about void pointers? Void pointers? Yeah, how that crap works. In what sense? What's a void pointer? I don't know. I was hoping you could shed some light on it. I was working on an operating systems project last night. There was a really bad casting going on. Oh, the type, like a void star. Yes, yeah. Ah, okay, so a void star is just a type. Void is a type that means nothing. It can be anything. So therefore, you can use it as anything. So if you don't know if you have a function that just returns something, and you literally don't want the type system to know for whatever reason what it is, it's a void pointer. So you can just say it's a pointer to anything. And then it's up to the person using that pointer to cast it to the correct type. So for instance, like the malloc function, right? So malloc, that makes sense, right? Malloc gives you a bunch of memory and returns random memory, and your program needs to know what it's actually pointing to, right? Because you told malloc, give me a point, give me the address of a memory location that has six bytes in it, or eight bytes in it, or 12 bytes, whatever you pass in as an argument, right? So malloc has absolutely no idea what type of data you want to put in that box, right? And so that's why it returns a void pointer, because it doesn't know, right? And it's your job when you call malloc to cast that to the right pointer. So when you de-reference the pointer, the compiler knows how to access things then. So you can do, yeah, I mean it's really just a type thing, but ultimately, right, it's just like any other pointer that we've seen, it's a value, it has a value, right? And that value is the address of whatever it's pointing to. Does that clear things up a bit? Sure. Sure. I thought you were talking about like smart pointers or null pointers or something, like these weird kinds of other stuff. Cool. So we looked at this, let's look at this example. I think this is where we left off on Wednesday? Well, I don't know what it is. All right, so we have our awesome C program here. We know that when we see, so how many declarations do we have here in this little chunk of code? Three declarations, right? So we're declaring three variables, X, Y, and Z. So when we see them, we need to create boxes for them. And we're going to bind the name X to this box. We have X, we've got an error, we've got a box. Do we know what the value is inside X? No. No, we don't know, right? It could literally be anything, right? The C specification says that it is undefined. If you try to read the value that's in there through, it could be implementation dependent, compiler dependent, day dependent, whatever. C says if you access that variable X before it's defined to it, then who knows what that value's gonna be? So we see X, and we're doing box circles. Do we care that it's an it pointer pointer versus an it pointer? No, it doesn't matter, right? Because all we care is that the value. There is a value, we know that there's a variable X, so there's a name X, and there was a location associated with that name X. That's what this declaration does. Now when we see the same thing for Y, we see a variable named Y, so we need to create some location and have that location associated with Y. Finally with Z, the same thing, right? Even though this is an integer, the other two were integer pointers and an integer pointer pointer, right? It's the same thing. It's still a name Z with a box associated with it. So now we get to X, so malloc. What's malloc gonna return? L value of R value, and address is an R value, and so it's gonna copy that R value and put it where? In the circle of the box associated with X, right? But what is malloc also gonna do? How does that change our diagram? New box, yeah, malloc is the way we as a programmer create new boxes. We'll actually see this as soon as we know the pointers. We're gonna look at the different types of memory allocation, but we know that here when we declare a variable X like this, we're declaring a place in memory where X is going to live, and when we call malloc, we're asking at runtime, give me some memory and give me the address of that memory. I wanna be able to use that memory. So it returns, let's say, I don't know, let's say it's at four, whatever, so we have a new box. It has some address, we don't know, and it can change that between different runs of our program, right? There's nothing that says it has to be fixed at four or it has to be a certain value, so we can say, here we'll say four, I think we're gonna now use symbolic values and call it alpha, just so that way we don't really care that it's a specific number. And then we're gonna copy four into the value of the location associated with X based on the assignment semantics that we've been studying and looking at now. We also draw an arrow. And what would you label that arrow? Just the, probably just the memory of this. So why do you wanna draw that arrow? Just cause. Just to have a reference to where it is. But how do you have that reference to it? How would you access it if you were programming it? Like how would you access this box from X? So what I put the label on that, if I wanna draw an arrow from here to here. Anyone else? We do want to do that and you'll have to do it on your assignments. What was it? Is it just an X? X, X is here, right? X is associated with this. Star X. Yes, it is there. My reader, where do you look at the slide there? You don't have to answer that question. It's not the teller one here, my reader. So yes, so we can draw an arrow here to say that, hey, when we dereference X, we're referring not to the value inside this box, right? We are referring to this location. Because we know that the dereference operator star returns a location, it returns an L value. So when we dereference X, we look up in the computer, which location has memory address of four, return that, and then that is the box that we're referring to. So this is how we can refer to star X now. Second line with Y, how does this change things? New box, right? Malak is new boxes, we're gonna get new boxes. Cool, it's gonna have some memory address. We'll say it's eight. Why did I choose a number that's four? Different than the previous box. Yeah, because what's the size of an eight star? Four bytes. Yeah, so it doesn't have to be like this. And actually it may not be, depending on how your Malak is implemented, it may not be exactly like this, but it doesn't matter. We know it has to be at least four away, right? It could be more, it can't be less because that would mean that it's overlapping. Cool, and where are we gonna stick the value eight, the return of Malak? In the circle of the box associated with Y, right? The assignment semantics are we have an R value, which is eight, we're going to copy it into the location associated with Y. So where would star Y point to? The new box. Now we can draw the arrow the same way, lay what's star Y so that we know when we do reference Y, this is what we're talking about. Let me say something like X is equal to the address of Y. This is valid C, can we do this? Yeah, so what is the address of Y return us? L value or R value? R value, some actual value. And we didn't give them any symbolic name, so we'll just call it address X, address Y, address Z. And so address of Y is the return what? ABY, the value, the R value ABY, and it's gonna copy that where? Exactly into the location associated with X, right? So we're gonna change this for here, now to be ADY. So now is my arrow of star X correct? No, no. No, where does star X point to now? The box of Y is the right one. Yeah, the box associated with Y. Pretty sure they don't have feelings, it's okay. Are you saying four is lonely now because it can't be referenced anymore? We'll talk about that next. We just need to know how the pointers work so that we can talk about how errors can accumulate. Cool. So let me say Y is equal to the address of Z. What's the address of Z? ADZ, so we're gonna copy that, put it in the location associated with Y, which used to have this eight, so we're going to remove this eight, copy an ADZ, and now we're gonna start Y point to it. The box associated with Y. Could we also add another arrow to this diagram? Maybe starting from, could we, could we also label this arrow a different name? Star, star, star, star X, right? Because X is a pointer to an integer. So we de-reference X once, we'll go to this box associated with Y, and if we de-reference this, we'll end up at the box that's associated with Z. So we could also add, you could add a double edge, or you could even just change this edge as star, star, X. But we say star X equals Y. So what does that do? What's star X? Star X, is it returning L value or an R value? What does this de-reference operator always return? L. An L value, so it's returning the box, a box, which box? Yeah, the box associated with Y, right? Where star X points to. So now I have L value equals L value, so what do I have semantics here in my assignment semantics? Yeah, we're gonna say, copy the value in the location of the right-hand side's L value, which is ADC, take that value, and copy it into the location associated with Y, which is Y. So this assignment save it to what? Nothing. It's weird that it does nothing, right? I mean, before when we saw it being nothing, we had X is equal to, what was it, star address of X, right? Which kind of makes sense because we can get rid of those stars, we're thinking of the star and the other sub-operators canceling each other out and we can see X equals X. But here we have two completely different variables, X and Y, and yet nothing changes, and we could just as well leave this line out, Y. Yeah, because star X refers to the same location that Y refers to, or happens to at this point in the program's code, right, at this point in execution, X points to the exact same box that Y refers to. Let's add some more complicated stuff. So when I say Z is equal to 10, what changes in my diagram? We're gonna take the value 10, copy it into the location associated with Z, boom, 10 goes there, that's all. So now when I print out star star X, what am I printing out? 10, so what is the location associated with star star X? The same location that's also associated with Z, right? This is also trippy, this is why pointers are very complicated, right? Here, on this line, I change the value of Z, and that changes what I'm outputting here when I dereference X, when I go star star X. Cool, then I do star Y equals 100, what is I gonna change? Z, the location associated with Z is also the location star Y, and so it's gonna copy 100 into the location associated with star Y, which is this location, it's gonna change this to 100, and then when we print out now the value of Z is it gonna print out 10? 100. 100, and that's exactly why, right? So even though here we change Y, because star Y points to the same boxes that Z refers to, we change that and Z is no longer has the value 10 like it did previously. Okay, so one thing we can say right now, so we need to find some concepts because these are very important, right? So star Y and Z are different names for what? The same location, right, not the same address, they refer to the location that also has, that is the same that has the same address, right? But so Z and star Y, what, is there another name for this box? Is there another way we can access this box? Star star X, yeah, so there's actually three names for this same location, and so we're gonna use the term alias for this. So star Y and star Z are aliases, what does that mean? Yeah, they refer to the same location, and so, yeah, super easy, I mean, easy definition. So what are the other aliases here? So we talked about star Y and Z are aliases, we said star star X and Z are aliases, what's another pair? Y and star X, yeah, are there any more? Could you just say X, Y, and Z are aliases? No, right, they each have their own location that they're associated with. And so part of the questions that you could be asked, right, is at a certain program point in this program, what are the aliases at that point? So why are aliases important? If that you've aliased stuff, your data's gonna get corrupted quickly. Yeah, think about if you have a list, right, have you ever had this problem where you have two pointers to the same list, maybe you pass that pointer to the function and then that pointer modifies your list and then when that function returns, your list is now changed from the original and you're like, what the heck happened? Who changed my list? Right, these could be incredibly frustrating bugs to deal with and to think about. And so another area that you may not be familiar with, so with static code analysis, we want to look at a program and say some guarantees and some facts about what it does. So in security purposes, we want to look at a program and say whether it has security vulnerabilities or it doesn't. The problem is, just looking at this code, if you were doing it without considering the pointers and the aliases, right, if I asked you here, what's the output of Z going to be without executing this code? What's the, what can Z be right at this point? What's it gonna output? It looks naively as 10 because you see Z gets set to 10 and then you say output to the value of Z, right? The problem is that in between those steps, star Y is the same alias as Z, which means that this actually changes the value that we're interested in. And so think about, you know, that's just one tiny thing in one, and what is this, I don't know, 10 lines of code maybe? Think about like a large program like the Linux kernel. Maybe that was too large. Even your project three, right? Trying to analyze and reason, I mean, it's sometimes hard for me to analyze and reason about it as a human when you bring your project three in, right? Because they're, you know, it's a complicated project. I'm not saying it's a simple project, right? So your code's gonna be complicated. And so for a static code analysis system to reason about these things and say, what are possible values for Z here? Is it ever possible, like for instance, one question you may ask here for Y, is it possible for Y to be null? So you'd have a null pointer dereference, which could be a security vulnerability or a use after free or some of these other security vulnerabilities. And so as the program grows in complexity, you have a lot more possibilities for aliases and so you have to reason about these things. Anyways, this makes analysis super interesting and also really difficult. Cool, oh, we already did this. Yes, start, sorry, X, start Y and Z. And start X a lot, perfect. Let's do, since there's not a ton of you, we'll do it and choose your own adventure. Do you want another example on this? Or do you want to go to memory allocation? All right, memory allocation, cool. You have homeworks to practice. Oh, I guess I can do that, that's not fair. Okay, memory allocation, cool. So up to this point, we've been talking in these abstract box and circle concepts, right? We haven't really been thinking, so we said that every location has an address associated with it, which makes us think of memory. But we haven't really talked about how the compiler, how the computer actually does this and where these locations live. So, from your experience, learning about languages and programs, what are possible ways that memory can be allocated to your program? What was that? What does it mean? What is the stack, a data structure they learned about? What is that, but what else? What's the difference between the stack and the heap? The stack is static, the heap is dynamic. Sorry, what? Stack is static, the heap is memory. I don't know that I'd agree with that. I agree that in some, depends on how you look at it. Yeah, so, I mean they do really get the layout there on opposite sides and so they grow towards each other. But what does that mean grow towards each other? Like in terms of memory, like you as a programmer, how do you know if you're using the stack or the heap? If you're defining memory rules, then it's dark. What was that? If you're defining variables, then it's dark. And if you're defining dynamic memory, then it's heap. What's dynamic memory? So, programs will see, separates a static memory and dynamic memory when it comes to the program. Yeah, but how do you use a programmer to use and allocate that dynamic memory? Maloc. Maloc, so Maloc gives you memory specifically from the heap. And what's that, your contract with the system when you call Maloc? What do you have to do? You have to give it the size. You have to give it the size, exactly. And then what do you have to do afterwards? You're done with it. You have to tell the system I'm done using this, right? So the stack you're allocating, so is it, so you said what variable decorations, is that what you said? Function calls. What does that actually mean though? Is it all variable decorations stored on the stack? Not global? Why? Give it a separate space. Yeah, cool, right? So the very first one, right, is, so basically memory allocation in the context of what we've been talking about until here, right, is how do we create new locations? And the key part, the second part of this is we talked about how do we reserve that address so that we don't have any locations that overlap? And we don't reuse locations, right? Think about if we called malloc and we got the same address over and over back from it, right? It's like, yeah, yeah, if you want to use memory, just keep using it, right? And so we have all of these pointers that point to the same box, and now they're overriding and changing each other, right? Okay, cool. So we either want, with this memory, like we saw, we either want malloc to create us a new location, or we want there to automatically be a location associated with the name, right? So what's the opposite? So allocation, right? We also want to care about deallocation, right? This is what we just said. We want to be able to say, hey, we're done using this, right? In a program, think about a program that you write that just would never return any memory and it just keeps consuming tons and tons of memory, and then at some point it crashes. We have lots of memory, it'll be fine. It'll be fine? You'd be surprised. So did that take us to the Ruby on Rails story? No. So the early version of Ruby on Rails, they had a memory leak in their program, and so it would just grow and grow and grow over time. So the guy who created it, DHH, I can't remember his full name. If anybody remembers, let me know. He basically just wrote a thing that kills the Ruby on Rails process and then restarts it every half hour. And that was just fixed. It's like, well, it grows slowly over time so you just kill it before it gets out of memory and just start a new one. That's likely. That was as fixed to that until they actually solved the problem. So, yeah, but it worked, right? They shipped the product that they needed to ship. Okay, so de-allocation is how can we actually release this memory and say, hey, we're done using it, right? You can feel free to reuse this location again. I'm done using this. Okay, so we saw the types. We actually talked about, I'm pretty sure all three. Global allocation, right? Which probably goes with what types of variables? Global variables, right? We need to be able to access them from any function in the program, right? So it would make sense that the allocation is done once and the outcaded memory is never de-allocated, right? So why is, can the memory never be de-allocated? It can always be accessed by any function in the program, right? So I think about global memory and this also ties back into scoping, right? Because that global memory is accessible to any function in that file and any other code that links to and accesses that global variable, right? So the program can make no assumptions about when you've actually stopped using this global variable because at any point in the program's execution, any function can access and change that value. Why is it only gonna be done once? Yeah, does it ever need more global variables than what we declared? So we declare like the Lexar on project two, right? Has an array with a 256 or 500, a character array called the current token, right? So remember the 500, 256, 200, all right. That's in global memory. So that gets allocated to the program. That's at a fixed, well, we'll talk about that. It's at a fixed memory location and that never goes away, but it never gets more. It only asks for 200 bytes. That's all it's going to get, right? So global allocation, it's great because you know it's always gonna be there. You know, everybody can access it, but the problem is it never gets de-allocated and you can't ask for more, right? Basically it's your way of saying I'm only gonna use this much memory and this is it as far as global allocation. Okay, stack allocation. So what's the stack in terms of a data structure? Last and first out, what does that mean? What kind of operations does it have? Push and pop, push and pop. So we push things onto this stack and we pop off, we're gonna get the last thing that we put on, right? So push, push, push, pop, pop, pop, pop, pop. Push, pushes and pops all the way through, right? Awesome, cool. And so the key thing here with stack allocation, this is associated as we'll see, we'll see examples with nested scopes and function calls. And it's basically ties in very closely to scope. So every time you access a new scope, right? We have new variable declarations and we saw based on our box circle diagrams we need somewhere for those boxes to live. And so we'll find out they live on the stack. And so anytime we access not necessarily just a new function, but also a new scope, whenever we access a new scope, when the program, sorry, executes a new scope, we allocate new memory on the stack. And then when we leave that scope we automatically clean it up and de-allocate it. And this is our contract with the compiler, right? This is the semantics that we've decided on with the compiler. We know when we declare a local variable called X that's an integer, we don't have to free that at the end of the function call, right? We know the compiler will take care of that for us. So stack allocation, you can think of the big difference between stack and global is stack is local variables and parameters of function calls as we'll see. And it's automatically de-allocated. So now the heap is where we come in as programmers, right? So with both of these, right? We still, both of these types of memory allocations are tied to what our program specifically says before compilation, what it does, right? Global variables, you declare upfront exactly how much memory you need and the system gives that to you. It's great, you need a gig, here's a gig of memory. Awesome, go nuts. But you can't ask for more. And with the stack, can you dynamically add local variables to a function while the function executes? Can you change the size of arrays that are as local variables? No, those things have to be static and have to be known beforehand, right? This is because of stack allocation. So because we're allocating these things on the stack, the compiler must know how much space do I need for this function on the stack? So you can think this is why what it goes through when it compiles your functions, it looks at all your declarations in a function or in a scope and then it says, okay, great, I know I need exactly 80 bytes for this function and so the function will change 80 bytes and reserve 80 bytes for you. But that doesn't give us really good capabilities, right? It doesn't give us the ability to deal with a context-free grammar that is 50,000 rules long, right? And with IDs that are more than, so that's actually the big failing of the project to Lexar because it has fixed hard-coded token size and it says no token could be larger than 200 characters when maybe the grammar doesn't necessarily or the Lexar doesn't necessarily specify that, right? And so we want to be able to deal with and to get more memory reserve based on the input to our program, right? Everything else is pre-in the compilation process but we need some kind of capability to ask the system, give me more memory. I need more memory, right? And the system can either say yes, you have memory or no, you don't and that's where heap allocation comes in. So heap allocation, right, is the system giving us more powers, right? It's giving us more power to allocate whatever we want but we have that flip side, that double edge sword where they say but it's up to you to free it when you're done with it, right? So you have to keep track not only of what you've allocated but you have to free it and you have to keep track of the size too in C, right? Nothing tells you how big that, I mean, it really only comes in effective strings but it's so important, right? And this is the same in C++, right? Whenever you have the new operator, this is exactly what's happening. New is using malloc, I believe, under the hood to do this allocation. It has to do some other stuff too but to set up that memory for you. What about Java? Is Java the same, yeah? New? Yeah, if you run a Java program in a loop and you just, well, make sure that may not. You could probably, yeah, you could probably set it up so it would crash but. Yeah, so when you call new, right, you are asking the system to create you a new object. That object has to live somewhere and if you think about it, what is Java running on? Is Java running on a Java machine? Like a Java CPU? How do you run your Java programs? Yeah, the JVM, what is the JVM? That's a version of the same, what is it? What's it written in? C. C or C++, it's just a program just like the programs you're writing. So when your Java program says, hey, I need some memory, it goes great, I'm gonna call malloc and get you some new memory. It's actually probably a little smarter than that but essentially that's what it does, right? It uses the same thing your program uses to get more memory. Well, so this is in, let's see if I can make a statement that these three types are in all types of programming languages. I'll say yes, and then somebody can disprove me. List, you can explicitly. You can assign the things. You can use the car and the CDR to generate, or you can use comms, that's what it is. You can use comms to create new list elements, I guess, probably other ways too. Yeah, that is functional programming languages, that's interesting. You definitely, yeah, that's tricky. And maybe prologue. Prologue's very different, yeah, I don't. Yeah, you probably don't have to think about that as much. Anyways, cool, okay. So our three types of memory allocations, right? So now I want to think about what are, from this program, what are the different types of memory allocations here, right? What are they actually going to be, right? So I have AX, global, that's going to be global. So what the compiler actually does, right, is it specifies and it decides a fixed memory location and says at 0804, 00B0, that's X. And so every reference to X will just access that memory location. So what about inside here? So when we get to this program, right, so we have this global X, we've set that global X to be 10. When we get inside here, what kind of allocation is this? Stack allocation, right? So it's going to be allocated as soon as we get inside the braces and then it goes away afterwards. Right, and so once we get outside here, we know that we've talked about pointers, right? Even if I had a pointer to X, right, that I had a pointer that had the value of what X used to be, is that pointer valid outside of here? No, right? Because after this scope, after I have left this scope while I'm executing, right, X no longer exists, it is deallocated. We told the system, the system automatically deallocated it for us, right? So we can go into foo, we can see we have another, what type of allocation? Stack allocation, and when we leave it goes away. And inside bar, so we call bar inside bar, we see we have X. So what type of allocation do we have here? Heap and stack, what are the differences? I mean, which is which? The malloc is the heap and X is on the stack, right? Which matches exactly to our box circle diagram, right? We have a box circle diagram for X because X, the variable X, even though it's a pointer, lives on the stack. And malloc creates a new location for us in the heap and returns the address of that, and then we put that address inside of X. We have two here, awesome. Please, all right, that was easy. Is it super easy to use memory correctly? No. No, no, we all know it's not. What are the most difficult parts? Remembering things. So what are some problems you've encountered when using memory? Second fault, what does that mean? In the terms you already talked about. Do you reference a null pointer? Yeah, I usually do referencing a null pointer, so you're trying to access a location that was not allocated to your program. That is the base definition of a segmentation fault. You tried to access a location that is not allocated to your program. So second fault, what else? Overflow, is that really a memory problem or a programming problem? What kind of overflow? Like you asked for too much memory. Like maybe you have a leak, you have a loop and you keep calling malloc over and over and the loop's infinite, and then you run out of memory and crash. It's definitely one possibility, what else? Index out of bounds. Index out of bounds? Again, does that have anything to do with allocation or you not knowing how big buffers and structures are supposed to be? Memory overriding, in what sense? Mm, yeah, that's super tricky to deal with, right? That's actually one of the reasons why I hate using global variables and I do not use them in my programs if I absolutely can't. Because you get into this situation where it's very difficult to reason about it. The program grows more and more and you start thinking, hey, why did this flag change? Or who changed this string? You literally have the entire program to think about. So I actually now, when I program things, I usually pass parameters into functions of all the variables that they're gonna access. Which is nice, because then when I look at a function, I know exactly which variables it touches and it never touches any global variables. So, cool. Okay, we've seen some good ones. So, kind of similar to the segmentation fault, right? We can have what's called a dangling reference, right? So this would be a reference to what? Yeah, memory that's no longer allocated to our program. Unfortunately, because of the way C and underlying systems work, accessing a dangling reference may not always result in a segmentation fault. But that doesn't mean our reference is not dangling. So we'll look at examples of this. We have a reference to memory that was originally allocated, right? That was given to us. But now it's deallocated, right? Think about two pointers that point to the same piece of memory that was malocked, right? We free pointer A, and then try to dereference pointer B. Because now we've pulled the system, hey, go ahead, reuse this. We're done with this memory. Now B points to that memory location, so now it's a dangling reference, if we... So the point is it is any pointers that now point to memory that is deallocated. Cool. What about garbage? It's talking about the quality of our code. We're garbage programmers who write garbage programs. I didn't get much of a laugh. I was joking. So what do we mean by garbage? On the initialized memory? Yeah, so it's memory that we've allocated, right? And so we know it can't be global memory because global memory gets deallocated at the end, right? It can't be stack memory because stack memory is automatically deallocated for us. But the idea is we ourselves have to call free on memory that we allocate on the heap. And as we saw in maybe the previous example, if we cannot access that memory, if we have no references to that address anymore, then that is called garbage. So memory that's been allocated on the heap, it's not been deallocated, but it's not accessible to the program. There's no possible way for us to access that memory location, which means we cannot free it anymore. Cool. Let's look at an example. So I have main. I have an input pointer dang, dangling pointer. I'm gonna do it again. Okay, this is an example. Cool. So we're gonna look at this how this works. All right. One of the key skills, I don't know if I know this. Maybe. One of the key skills you're gonna be able to do definitely in this class, but in general is be able to look at code and know what it's gonna do and execute it, right? You should be able to think like a CPU, right? And that's why we're learning all this semantics because you know exactly what it's doing. You even are given tools and you have ways to help you think about those with box circle diagrams. So cool. We're gonna create this dangling. Then we're gonna call food, right? So what is this gonna do? Is this valid? Can we do this? Yeah. Yeah. Valid syntax, right? Cool. So I go into here, I say if x is equal to 10, where is x allocated? On a stack is 100. And we're returning the address of x. So what is the address of operator return? The address of the location that x is associated with, right? Do we know what it is? Not quite, but can we return it? We return it and then we get back and now that we're here, what happens? x is deallocated, but what does function food return? The address of x, which is just a value, right? It's an R value. We're gonna return that. Let's say it's 10 or whatever, it's four. And we're gonna copy that into dang so that dang now has the value of four. So at this point, do we have any garbage? No. No? How do you know? We haven't allocated any memory. Yeah, this is a minor trick question, right? Just keep going and tell us. Do we have a dangling reference? Yes. Yes, the appropriately titled dang variable, right? Is now pointing to, it has a memory reference in it that does not exist, right? x no longer exists now. So, I can now, but I can still print out, right? Dang is just a value, right? So I can print out dang and start dang. So what's the percent p mean in the print out? Pointer. So what does that mean? It's gonna output it as? What was that? Extraces. Yeah, so it's gonna output it in hex and it's gonna append zero x before. It just makes it really easy to look at. But remember, it's not the address of dang that we're printing out, right? It is the value inside dang, inside that box. And then we're also printing out as an integer dereferencing dang. So what could that print out? It could print out 100. Couldn't it also print out literally anything else? Yeah, because we have a pointer to nothing. Or it could crash, right? It could cause a segmentation fault. And actually, even though it's counterintuitive, a crash would be better, right? Why would a crash be better? Yeah, we know that we have a dangling reference, right? That would, we would now know that we have a dangling reference. We have a bug in our program. So it's gonna print this out. It's gonna call bar. It's gonna set y equal to 10,000. z is zero. Gonna print out y and z. Anything wrong here? Nope, two stack allocations of y and z. Print those out, good. And then again, we're gonna print out dang and star dang. So this is why this is so tricky. Should we expect that they're gonna same things? Let's think about this. Should we expect that dang is the same? Yes, yes, yes. Yes, right? It's on our stack. There's a value that was the address of x that we've copied in today. Can we assume that star dang is going to be the same thing? No, and here's the really tricky thing, right? Not only could, so a, the spec says if you dereference a dangling reference, there's no guarantees on what that value is. That behavior is undefined, right? So that output there is undefined. But intuitively, you think, well, okay, maybe that is a problem, but if I access that again, it would still give me the same value. So we'll see if that's the case here. So you can compile this on your program. It actually, so on the compiler I was using, it does give a warning and it says that you're returning the address of a local variable, but it does not give an error. So you can think that, yeah, in this case it's not good, but we could actually code this up in other ways that the compiler would not be able to warn us that this was happening. Like we could pass the address of x into another function that returned it back to us, and then that's what we could return, right? I don't think it's sophisticated enough to check that data flow. And then we could run this, right? This program actually compiles, and when we run this on, I think this was on a CentOS67 machine, a 64 bit tube, so you can see because of the huge address, right? I see this output of dang, and then I see 10,000 and 0, right, which is from bar, and then I see the same thing, but now zero, right? So it's weird that first, so it did actually access what I thought, but somehow it got changed to zero, and the super crazy thing is I ran this same exact program on this machine on a Mac. I compiled it, it also tells me the same error. I can run it, I obviously get different memory addresses with different systems, still 64 bit, but now the value changed to 10,000, which is the local variable inside of bar. So, huh? Yeah, it's actually really cool, so we're gonna actually look at how all this happens, but I believe, yep, time to go. Cool, and because you all came to class, and none of your class mates did, no class on Monday. See you on Wednesday.