 Welcome back to 105. So pointers. Everyone okay with pointers? You was kind of point at things, not too bad. So pointers probably one of the most confusing things when you start C programming. So today we're going to take pointers up another level. This lecture is basically full of things not to do. So if you pay attention to the lecture and see what I'm doing and just don't do the things I am doing today, you will be okay. So this lecture may or may not go very poorly because I'm going to do some weird things. So let us begin the weirdness today. So any problems with our function we oops any problems with the function we wrote yesterday. So this function computed the averages for the grades. Everyone okay with this function? Not too bad. If we want to add a new grade, easy. We just have to just add a new grade. Don't have to remember to update anything else. Really, really good. So everyone's okay with this, right? So let's one of the like tenants of good programming is well this little loop here, calculating a sum. Well if we might want to do that in multiple places that seems like a useful little small piece of code that I might want to reuse, maybe I want to actually move it to a function. So let's just move that code to a function and just rewrite it a bit. So let's create a function called sum and well here we're computing the sum over an array and in this case it's an array. So maybe I'll give it a general name. I'll just say it's an array and I don't have to tell it how many elements are in it. So after that I'll just take this code and essentially put it here. So actually this still needs grade length. So I don't have grades anymore. I'm trying to be general. So instead of grades length I have array length and I want the array length of the array and I'll just trade change essentially grades to be array. And then I'll return the sum. Maybe I want to call it a different thing than sum here just so I don't confuse myself. Let's just call it act short for accumulator because I don't feel like spelling. And then here to get the average it's the sum of the array or of the grades array over this. So it seemed reasonable, right? Any problems with that? I just kind of moved it out into the function. So here I still have not recompiled yet. So this is my original version of the program that does not use a sum function. So here the average is 78 and you could verify that yourself that that is indeed the average. So if I just move this into a function I expect the same result, right? I expect to see 78 as my average. So let us recompile our code, make sure we saved it. So recompile our code and then we'll run it. What the hell? What? All I did was move it to a function. Oh geez, all right. So now we have to explain this. So this is why sometimes C is hard but after we explain this and why this is wrong we'll learn something about how computers work. So program looks the same, output is wrong. 78 suddenly turned into 31. What the heck happened? So we might get a few clues just because of 31. So if we look at the code how is it possible, anyone see how it's possible to get 31 out of that? So this is part of debugging programs is just to try and reason about what actually happened here. So the average got calculated wrong. So this result of this was 31. My grades length didn't change so my grades length was the same as before. It's 5. So what does that mean about my sum? Yeah, yeah. So it kind of looks like it only took the first two elements because if I do, well if I do 75 plus 83 and then divide that by five, well then I get 31. So we can kind of guess that it just added the first two elements which might mean this array length was 2 and not 5 as we expected. Why would that be? So yeah, we correctly deduced only the first two elements seemed to actually get used here. So what we might want to do is see, hey, well in here in this function, whoops, in this function what is actually the size of the array? So let's see printf. So sometimes for debugging purposes, it's good just to have some printfs. So the size of the array, we'll just print off what its value is, size of the array. So if we do this and we try and figure out what the heck is going on, it says size of the array is 8 bytes, which would be like two integers maybe, seems like something else is going on because before here in this grades, whenever we did the size of grades, it was 20 bytes, right? Because each int is four bytes and we have five of them. So something real strange seems to be going on here, like thinks our array is four bytes now, or sorry, eight bytes now. Kind of weird. So to explain why that is, we have to explain how C actually stores arrays in memory. And it's a good thing we had a pointer lecture before the array lecture. So how arrays are actually stored in memory is for the purposes of this course, it essentially just picks a random address a to start the array. And then all the elements of the array are going to be beside each other in memory. So the size of the element here depends on the type. So I'm using an int here. So it's four bytes. So it picks some random address a that I just highlighted in green here, just so we can keep track of it. And that will be the starting address of the array. And then all of my ints are going to be beside each other in memory. So the address of the first one is going to start at a, or we could just say a plus zero, still a, and then those next four bytes would all hold the value of the first integer. And then the second element, which would be at index one because these are arrays, which are zero indexed, would be at address a plus four. So it moves four bytes over to get to the next integer because the size of an integer is four bytes. And then the next element would be four bytes after that, and then four bytes after that. And here I'm using hex. So remember, hex, it's base 16. So it's still a multiple of four. So if this was in decimal, this would be like a plus zero, a plus four, a plus eight, a plus 12, a plus 16. But because this is in hex, this means the same thing. So remember, in hex, a is 10, b is 11, c is 12, d is 13, e is 14, f is 15. Wow. Okay. I know my alphabet. Hell yeah. All right. So, and here, yeah, this would mean in the one column, it's zero and the 16th column, it's one. So one 16, that means it's 16. So in order to keep track of an array, all c does because it knows all the values are going to be beside each other in memory. It just uses a pointer to start the array, and it only keeps track of the pointer to the start of the array. So your array is actually just represented as just one single address. So any questions about that or how that's laid out? So it's all beside each other in memory. You can count it up. It's 20 bytes in this case. All right. Cool. We are experts. So because the array is essentially a pointer, remember, arguments are passed by value. So arrays are not passed by value because c thinks an array is just a pointer. So since c thinks an array is just a pointer, what we might think is that, hey, it gives that function a copy of the entire array, but it doesn't do that. So it only, if it did, it would have to create a giant copy of a new array. C doesn't really want to do that. So instead, what the function gets is a copy of the starting address of the array, and that's it. So a type like int, like int, and then an array of ints, well, if you pass it to a function, it just gets replaced by a pointer to an int. And you might find this referred to in some things as array decay. So as soon as you use an array, like outside the function you declare it in, it just gets converted to a pointer because all c cares about is what the starting address of that array is. So in this case, we got size of array equals eight in that sum function because in that function, array, that argument name is just a pointer. And we all have 64 bit machines now. So that means the size of any address is 64 bits, which is eight bytes. So 64, divide, like 64 bits, divided by eight bits per byte, that means we have eight bytes. So any type that ends like with a star or an amp, or an ampersand, an asterisk, whatever you want to call it, that just means it's an address. And for the purposes of this course, we can just assume that any address is going to be eight bytes because we're on a 64 bit machine. And then question is this also known as a shallow copy of the array? And kind of in other languages, you might consider that if you ever heard that term in C, C doesn't really have a concept of the it just thinks arrays are pointers, and you get a copy of the pointer. So because that function just gets a like the starting address of the array, since we know a, which is the value like the value of that pointer, then we can actually address any element because, well, we know that all of the values are going to be beside each other in memory. So again, if we have, you can kind of see the formula here. So at index zero, our first int that would be starting at the, where the array starts. And then the next value that is an int would be four bytes after that. And in this case, it's 83. And then we'd have another one four bytes after that, and then four bytes after that, and then four bytes after that. So you can kind of see a formula here. So given like the starting address of the array, and some index i, so in this case, this would be the value of i. Typically, we just use i as a short form for index. So seems like the address of the element should just be like a. So the starting address plus the current index times like the size of an int. So in this case, size of an int is four. If we had an array of doubles, then, well, doubles take up eight bytes. So the addresses would be a bit further spaced apart because doubles are bigger. So turns out that if you do this, you will get something weird. So see what it will do is whenever you try to like, in this case, this is a pointer and we're trying to add something to it. So see has special rules for pointer arithmetic. So the rules for pointer arithmetic are that if you have like a plus i, that results in this address above. So see will kind of do you a little bit of a favor, and it automatically uses that size of operator for you. So it knows, based off the type, what that pointer type is pointing to, and it will do the size of it for it. So if I write a plus i in C, the address I would get is if I did that. So this would be the address like just in bytes. And the result I would get I would assume is just an address. But that address will actually be the result of this C expression. So C will go ahead and do it for you. So what does that look like? So for pointer arithmetic, we can only do addition and subtraction. If you try and do multiplication or division, it gives you an error and it refuses to compile your code. And it might question your sanity while you're doing that. So if we have something like int star a, so we have a variable called a, which is a pointer to an int. If we do like a plus i, that results in the address a plus i times the size of an int. So it would be four. If we had a double pointer, so variable called b, which is a pointer to a double. If we do b plus i in C, the resulting address we will get is like b plus i times eight, because that is the size of a double. If we have something even weirder where we have char star star. So we have a pointer to a pointer to a char. So in the case we have that, well, C plus i, that will result in, whoops, that's a typo here. So it will result in C plus i times eight, because that pointer is pointing to a pointer and the size of a pointer, which is just an address, is always going to be eight bytes. If, however, we do, we dereference it. So the type of dereferencing C is just going to be a char pointer. So if we do a char pointer plus a number, well, it will do, it will move it over the size of a char. So the resulting address will be like whatever the value is at C plus i, so the index times the size of a char, which is just one. So we can do weird things with pointers. But for that, don't have to worry about too much right now. All we really need to know is that C will actually guarantee the order of the elements in memory for us. And what that guarantee is sometimes referred to, you'll hear this so many times after this in every one, you'll just talk about forever. You'll say that elements in the array are contiguous. All that means is that they are together in memory, there's no gaps between them or anything. They're all in a row. So they're all beside each other. That's what the word contiguous means. So in this case, there's no wasted space, we have five integers, each integer is four bytes. So in total, we have 20 bytes, and we just use 20 bytes in memory in a row. So that would be like the addresses A plus zero all the way up to A plus 14 in hex, which is just how you write 20. And this is a normal range in mathematics. So if you write out a range like this square bracket means inclusive. So it starts at A plus zero. And then this parentheses, that means exclusive. So I don't use the byte at this address, I use the one just before it. But you can count it up. And see that in other words, it's the addresses A plus zero all the way up to A plus 19, which is 20 bytes if you count it. So questions about that. We are getting in the weeds here. All right. So with this weirdness, we can actually just use pointer arithmetic to access any element we want. So assume that we have our in array called grades. So again, that's how they're laid out in memory. If we just take grades, so our in array is called grades, it's essentially a pointer to an integer. So if we do grades plus one, that will actually give us the address of the second element here. So we start at grades, and then we go over one element. And since, you know, they're integers, we would get exactly this. And then if we want to get the value at that address, we would just do grades plus one, and then de reference all that, and then we would get the value 83. So this does this look familiar to an array index at all should look pretty familiar. So turns out that the syntax for just accessing a value of array is just for your convenience. So if I do grades and then square brackets index, that's actually the exact same thing as doing like pointer arithmetic on grades, if I do grades plus index, and then de reference it, I'm going to get exactly the same value. So the only reason they came up with this syntax for accessing elements of an array is because pointer arithmetic is bad. So especially in this course, if you have to do pointer arithmetic, you are doing something wrong. So the only pointer arithmetic you should be doing is with grades. And well, if you get an error with it, it's because the index you use was not valid for the array. And well, under the hood, it uses pointer arithmetic. So you just started pointing to just some old random address, and some weird things happened. So even though these are equivalent, again, you should probably just write what you mean and always use the array syntax. So with that, so the size of the array, like how many bytes it contains is only valid in the scope of its declaration. So in here, yeah, otherwise, if we try and use array in a different function, there's pointer decay because it doesn't actually know how big it is a function can be reused. So you can call it with different sized arrays if you want. And it can't tell the difference. So usually if we have a function that uses an array, well, we have to tell it what the size of the array is or what the length of the array is. So finally, if I go back and I rewrite my sum function correctly, I should just add another argument that says, okay, I need to know the array length. So that is something you also need to tell me when you use this function. So our sum function should actually look like this. So let's go back and fix that. So what we should do because, well, we can't figure out the array length here because we did not declare that array in this sum function. So instead of trying to calculate the array length with that little macro, I should just add a parameter here that's just called array length. And then in here, when I call some, I should pass the grades length. So I need to make sure that when I use this function that the array I pass the length is actually valid for it. So if I now run that, I finally get my average 78 after all that work. So this is just a cautionary tale that if you write a function, and it has an array there, and it doesn't have a length associated with it, you have messed up. So you always need to know what the length of the array is. Otherwise, you cannot guarantee you will be within bounds, and some bad things will happen. All right, so add a length on if it's not the function you declared the array in. Everyone good with that? Everyone remember that? So I just made a fool of myself for 30 minutes explaining what not to do. So do not fall into this trap. Otherwise, it will be weird. But now you can kind of explain it. So what other fun? So this is slightly beyond the scope of this course, but I'll explain it anyways. This won't be test a dog Xamarin anything. So this is just so we can even begin to reason about the carnage I am about to unleash. So and I saw some people were actually interested in this. So and asked about that. So there are some rules where variables are in memory. So the addresses for like variables and functions, the variables and functions, we heard the term global variables. Well, if there's global, there's probably local. So variables within functions are called local variables. And they start at a randomly picked address. SP short for stack pointer. You don't have to know what that is. You will figure it out. In your architecture course, you will take hopefully in second year. So just know that it is just some random address where see is going to start using memory from. And it's random for well, it's random for security reasons, because otherwise, you might be able to guess some addresses of stuff and things would be bad. So there are rules after you have that random SP, where the variables will be put in memory and where they're going to be put are in a certain order. And it will be at lower ad addresses from the initial stack pointer. Sometimes will be space between the variables. Sometimes they'll look like a raise. Sometimes not. You can't guarantee you can't guarantee if there's space or not. There's a bunch of rules for that that are much more complicated than we want to get into. But for now, there could be space between variables, no wasted space between elements of an array. So and even for these, like the address is getting lower as we create new variables and functions, that details may vary between CPUs may even vary between operating systems. So this is mostly tailored to what like 99% of the things do. So this program is dumb, do not do this. So what this program is going to do, I'm actually going to show. So here I'll label it memory. So on the right here, these are going to be remember when I was tracing through the program before I put the the variables that were in scope here and active. So they will actually be somewhere in memory. And now I will actually just give them addresses because we have to do some silly arithmetic. So turns out on this machine. If I declare in x, okay, well, I need four bytes to actually hold the value of an integer that I'm calling x. So after running that line, the address of x will probably be something like I just made up some addresses, but might be something like one ffc. So that's what bite it starts at. And then it goes to like bite one ff d e d e f. Yeah, d e f. And it has the value currently one. So if I declare an integer called y, well, the rules are that the address it will get will be lower than that one. So turns out on my computer, it will actually be beside each other. So it'll get address like one ff eight, which is only if you just look at the difference between these numbers, they're exactly four bytes away from each other. So like, the last thing here is 12, eight. So they're only a difference of four, which is the size of an int. Then here, I have, I declare zed, zed gets a lower address, it might be beside, it might be three. Then I create a pointer, which is currently the address we assign it is the address of zed. So after that, well, it needs to live somewhere in memory. In this case, there's actually a bit of a gap here. Don't have to know that because it's not going to be important. So p is actually the address actually refers to wherever zed is. So this address would actually be the value stored there would be one ff four. But for the purposes, of course, we never actually have to care what the actual address is. So whenever you have to reason about it, easier just to write a little arrow of what that value is pointing at. So any questions where we are so far before we do some arithmetic? Okay, so if I execute this line, so I do p plus two, and I dereference it, and then I change whatever that value is to four, what do we think is going to happen in memory knowing point knowing a bit about pointer arithmetic now? Yeah. Yeah, x is going to change to four. Shouldn't do this. Don't do this. It's weird. So this happens on my system. So in this case, if we do p plus two, because these variables are exactly where they are in memory. Well, if we take the address of z, which is this, and then add two to it, that means it's going to jump forward to integers or jump forward eight bytes. So in this case, well, this four plus eight is going to be 12. So it's going to jump here, or just skip one two, because they're all integers and they happen to be beside each other. So now p plus two is a pointer that points to x. If we dereference it, now we're changing the value, we're accessing the value of x, which is one. And then we change it to four. And then now when we print off this, we get four, two, three, which is weird. So here, just to show you that you actually do get that, we get four, two, three. Turns out, turns out this is even consistent between compilers. Actually, I'll leave that. So sometimes this actually depends on what compiler you use as well. And yeah, so question, can I explain the pointer changing x again? So if we go back here, so p here is currently pointing to z. So in this line right here, when we do p plus two, because that pointer is a pointer to an int, and we add two to it, that means we're going to jump forward two integers worth of bytes. So we're just going to jump forward two integers. So it happens, all these integers are beside each other in memory. So p is currently pointing to z. So p plus, so this is p plus zero, and then p plus one will point here. p plus two will point here. We'll point here. And then once we dereference it, that means we access this value. So this value is one. And then we reassign that to be four. So after this line, x changes from one to four. And how I got these, how I got the addresses, so how I got those addresses there, we just followed the rules of C. So the rules of C give you this. And the rules of C, well, actually the rules of C just say that the address of this will be lower than, or yeah, the address of this will be lower than this. After that, C doesn't say anything, there could be space between them, they could not be. Just so happens that on my computer with my specific compiler that I'm using, they happen to be all in line. So if you run this silly code on your machine, you might get a different result. And you might point somewhere else in memory, which would be even worse. Yeah, so it is plus eight bytes. So here, because it's pointer arithmetic, it does that size of the, and factors that in for you. So p is a pointer to an integer, so p plus two will move forward two integers. Or if we think of it in terms of bytes, it will move forward eight bytes for us. So that's funny. Basically, too long didn't read, don't do things like that because this happened on my machine might not happen on yours. And we'll see some other weird examples. So that data structure that stores values that we define in functions is called a stack. So notice that in all examples, the way it kind of worked is we added values to the top. So every time we declared a new value, it went from the top. And if we do a function call or anything like, and if we return from a function call or something like that, we get rid of them from the top. So it's called a stack because if you've been to the cafeteria, do they still have those spring loaded things of plates? So they got the name from that. So if you have a plate on that spring loaded stack, you put the plate on the top. And then the last plate you put on to the stack is the first one you pull off. So that's kind of how memory works. If you don't understand that and don't see how that relates to memory, don't worry about it. Again, you learn that in the computer architecture course. But this is why the rules are like they are. So again, don't have to know these rules. And in that previous example, I even had some wasted space between Z and P didn't actually have to be there. Yeah, and then question for some reason, if there's space in memory between X or Z between X, Y or Z and Y is a double, then it will still work. So in that case, if we have a different value there, we're probably going to like land in the half, like half the double, and we're going to get a really weird value. So if we're going to get some more weird values today, so don't worry about it. All right. So now you might ask of like, oh, okay, well, if an array just gets converted to a pointer, why don't I just use pointers as the type in the functions instead of just arrays. So why instead of some in array, and that's actually an array, why don't I just say int star array, and just cut out middle man and don't have to remember. So they do mean the same thing, but as humans, the intent is a bit different. So if I write int array, and then the square brackets here to indicate that it is an array, even though it's a pointer, it actually conveys a bit more information. So it's a pointer that actually points to multiple contiguous values. So it might point to more than one thing in a row. Well, if I just have an int pointer, like a pointer to an int, that usually conveys to the reader of the function that you're just pointing to a single value. And if you do pointer arithmetic on it, you have screwed up badly. So this basically means, well, you should do, you can do some pointer arithmetic on it, you should just use the array indices. And I should also tell you the length of the array. Alright, yeah, we have some more questions with some more weird stuff. Don't worry, we'll do some more weird stuff. But also, beware variable declaration rules are also strange with pointers. So if I do like int star x comma y, you might think that I get two pointers, one called x and one called y. Well, turns out in C, just to make your life difficult, it does not carry the star across commas. So the type of x is actually a pointer to an int. And the type of y is actually just a plain int, it is not a pointer. Don't ask me why these rules are. So if you don't want to remember these rules, which I don't, just declare one variable at a time, and then you don't have to worry about what the hell happens with the comma, because I don't know about you, but I don't want to remember more than I have to. So when you're writing this, just go ahead, write one variable per line. Alright, so usually we want to initialize variables to a safe value, like for ints, we initialized it to zero. Otherwise, we know that we might just get a random value, and if we accidentally use that, really weird things will happen. So same rule applies for addresses or pointers. They're the same thing. In this course, you should probably only set pointers to the address of something. And if you don't know the value, you probably screwed up somehow, or you probably need to rethink something, or you need to be very, very careful, at least. So if for some reason you have to give a pointer some value, there is a special value to give a pointer, and it is called null, which, well, you might think of it in math, means empty or zero or, you know, nothing there. So why you want to use null is because it has a few fun things with it. So if I screw up my pointer arithmetic and point to somewhere weird in memory, and I dereference it, I just read some random values. Well, if I dereference null, it's guaranteed by the offering system that I'm going to cause an error. So it's a special address. It's actually the address zero, and it's always invalid. So if I go ahead and write a little program. So all I do is I have a pointer to an integer called p. I'll set it equal to null. And then I'll try and read the value at that address by dereferencing it. If I go ahead and I run this segmentation fault, this will be the bane of our existence. So segmentation fault means I try to read some invalid memory. So in this course, well, in terms of like pointer arithmetic and stuff, segmentation folds are actually your friend, because they stop your program dead. And they indicate that something really, really bad has happened. So there was a bunch of numbers there don't have to understand them. All we need to really know is that our program has like crashed, and it stopped immediately. And we have likely screwed up our memory somehow. So they're a good thing, though, they're easier to debug because instead of just doing random things and just carrying on and who knows what your program does after that point. Well, it's much, much easier to just debug a segmentation fault than just some random value happens and some random things may or may not happen. So there's a tool called Valgrind that may help you, you're not required to use it, but it may be helpful, especially if you're using pointers and you see segmentation fault, you might wonder where it comes from. And this will tell you exactly where it comes from. So all you have to do is use it on ECF, it should be installed. All you have to do is type Valgrind right before. And if I do that, it has a bunch of output that you won't see, because something weird happened on my computer. But all you need to know is that it will tell you an error like near the top invalid right of size four. And it tells you what line it happened on. So in this case, it told me line six had the error. So it means well, I screwed up at that point. So that's when my program crashed. And I can actually go ahead and probably debug it from there. Oh, I did something silly. Alright. So another thing to go back to scope and tie it together with memory. So these local variables only exist when the function runs. And we can do some dumb things with these two. So here we have it. So here at our main, we declare a pointer called P. So it's a pointer to an int. And then we call foo. So in memory in our main, well, it'll create space for P. And we're not going to know the value of it yet, because we have to do this function call. So we would jump here, doesn't take any arguments, we don't have to copy any values or anything like that. This function declares an integer called x. And it would go in memory. And it would live in foo. So we set it equal to one. And now we return the address of x. So we return a pointer where the arrow points into x equals one. So points right at x. But after we return from foo, we go back to main and we resume. So after foo returns, the rule R is that the variables are now don't exist anymore. So my pointer is just kind of pointing at some random thing that is not guaranteed to exist anymore. So now if I dereference that, well, it's just pointing at some random memory. And this is, C does not like this. So if you do where you point at random memory where you can't reason about what it points to, C is allowed to do whatever the hell it wants. So this is where it will get interesting. So here is that code. If I go ahead and run this, I get one. So I still get the value. So turns out in this case, I get lucky because, well, that memory didn't happen to get reused by the time I printed it. So it was still seemingly valid. But turns out that's not always the case. So I just got lucky that it was still one. So in this case here, let's make sure let's change it to two. Oops. So I got lucky and it happened to still be pointing to that so it's still two. So turns out because it's pointed at something invalid, we don't know. So even if we do like a print, let's do a printf here, give it a big lol because this will do weird things. We compile and run that. Also, I did not pick that value, but that's very funny. That's total coincidence. So C is allowed to do whatever it wants. So it turns out printf is going to have to use some memory and it uses some memory and because I'm pointing at some random address, I just read whatever's there. It happens to be 69, which is hilarious. But turns out that this is not even consistent between compilers. So I used a different compiler. So in this, let's go back so it's two. So if I use the same compiler you all are using like GCC and I compile it and I run it, I get a segmentation fault. So it's not even consistent across compilers. So if you are pointing to random memory, C is allowed to do random things and you will never ever ever be able to reason about it because it will likely change between what compiler you use between what computer you run it on. So this is what makes programming hard. So do not do the stupid things I'm doing today because you trying to figure out why it just printed 69 randomly, not going to do it. But the whole reason is because we are pointing there to just some random space out in memory. However, we can return pointers that are still valid. So here, let's take a function called like max pointer. So we take two pointer arguments and we dereference them to figure out which value is bigger. In the case of a tie, we'll return the one on the left. And if the value at B is bigger than the value at A, then we'll return the pointer for B. So we're just returning the pointer of whatever pointer is pointing to the biggest value. So in main, I declare in x, assign it a value one. So in memory it would look like this. Then we have a y. We give it the value two. And then we call max pointer. So that is a function call. And also, we declare a pointer to an int called P. So we'd reserve some space for P, do the function call, and while it's copied by value, so max pointer will get its own variables and the initial value will be a copy of the address of x and a copy of the address of y. So in max pointer, A points to x and B points to y. So if I run this function, well, if I dereference A, so here the value is one, one is not greater than the value at B. So in this case, this would be false. So one is not greater or equal to two. So because this is an if statement, we'll go into the else and then we'll return B. So the result of the function is going to be a pointer to main's y. So after that function call returns, because it was pointing to something in main, and main is still running, it's still pointing to something that is valid. So this is okay. And if I print the max and I dereference it, I'll get max is equal to two, which is what I would think. But I'm using pointers in this case. So a quick questions about that. Alright, and again, you can ask on discord and stuff because pointers, pointers are fun. So to wrap up today's comedy of errors with pointers, just be very, very, very, very careful with them, only use them when necessary. So usually you only actually have to use them. If a function needs like multiple return values, so it needs to set multiple values that you want to access and whoever called that function, you should try and write functions that just need to return one value. So you just return that value, and then you don't have to worry about pointers. The easiest way to use pointers is to avoid using pointers. So scan F had to use a pointer because well, we could have multiple format specifiers and can set multiple values at once. So it needs to use pointers, anything else doesn't really need to use pointers and variables they only exist in memory at runtime, while that function is running. If that function is running, not running, then those variables do not exist. And you should not point at them. Otherwise, you get that really weird problem I had, we just get random values. So with that, just remember, pulling for you, we're all in this together.