 Welcome back to 105. So this is last lecture until our two-day break. So again, don't come to class on Wednesday and Friday. Do come to class next Monday. So let's get into this because we will need this when we start talking about Lab 9 stuff and it might be useful. So structures. What are structures? So in C, we can group together variables with in something called a structure. It is a new type and of course it's C. So they couldn't have just called it structure. They called it struct. So they made it shorter because everything they had to make shorter. So the syntax to define a struct is, well, you just type the word struct, then give it a name. So name representing what you want to call this group of variables. And then inside of the curly brackets, you can put as many variable declarations as you want. So as many variables as you would like to define, you give them a name. So similar to an enum short for enumeration, well, you define a struct just below the includes and typically you do not do it within a function. So we'll spend the rest of the lecture talking about why we would use this, why it makes our life easier and some motivation behind actually using it. And if you've seen like Python or C++ or Java or anything like this, this will be familiar to you. Essentially, they just took the idea of a struct and made it a bit easier to use, but at the end of the day, it is the exact same thing. So let's start off with a simple task. So we're just going to calculate the distance between two points. So I could write that as a function. So if I have two points, right, a point is an X and a Y. So if I want to calculate the distance between two points, well, I could represent them as X1 and then Y1 for the coordinates for the first point and then maybe X2 and then Y2 for the coordinates of the second point. And then if I want to calculate the distance between those two points, right, it's just the difference between the two X coordinates to the power of two. So I could write pow, you know, the difference and then 2.0, so that will just square it. And then plus the difference between the Y coordinates, so Y2 minus Y1 to the power of two. So again, we get some review using our math function. pow is just taking this as the base to the power of whatever the second argument is. And then in order to calculate the distance, I just take the square root of all of that. So I just give it to the square root function, which of course they called squirt, short for square root, because of course they did. And then if I wanted to use this, well, I could, in my main, I could define points X1 equal to one, Y1 equals to two, X2 equals to four, Y2 equals to six. And then I could calculate the distance and then we can review. We can go ahead and just print it to one decimal place because it is a double value. So if I go ahead and run this, they get 5.0, that's the distance between them. This should be nothing new, right? Any questions about this program as written? Fairly straightforward, we're just gonna make it much, much better today. So we're good, we're good, all right. So we could have written this at like, what, week, or lecture like six, something like that. So for a function like that for distance, not too bad to remember which variable is which. I have an X1, I have an, or Y1, I have an X2, I have a Y2, not too bad to keep track of them. But what about if I write a function that just like prints a point? So I just write a function just called print. I give it an X and a Y and then it will just print off the coordinates just rounded to the nearest 10th. And while this is a bit awkward to use and might lead to errors. So just naming variables X1, Y1, Y2, kind of a pain. If I accidentally write something like print X1, Y2, that's one coordinate from one point and one coordinate from another point. So that probably doesn't make sense. It's really easy to do here. Might not be one of the two points we intended and debugging it might be confusing because you just don't expect that. So since we learned what a structure is, we can just make a structure that represents a point. So we know a point is like a logical thing. So a point is a coordinate. So I could just create a structure called point and then inside of it, I'm allowed to make as many variable declarations as I wish. So a point I'll just say consists of an X and a Y. Again, they're going to be just doubles. So this will create a new type called struct point. And then you can just use that as if it was an int, a double, a char, a bool, whatever. So you can create a variable with it, but instead of just saying its type is one of the things we've already learned, you could create a new variable by saying struct point and I could say P1. So I could create a point called P1 and it would be the type of it would be the struct point that has an X and a Y. So to access the inner variables or to access the variables within that structure, usually called fields or members by the compiler. So you might see some compiler messages where it calls them members. You can access them by just typing or after the name, just putting a dot. So for example, if I want to access X of P1, it's just P1.X. If I want to access Y, it's P1.Y. So questions about that, we just get to group them a little better and then we can go ahead and rewrite our function. So first I'll get into a common trick you will see that is arguable whether or not it is clear or not, but we saw like type def before when we had enums. So the syntax of a type def just in case we missed that one on Friday, is you can type like type def is a keyword. So you can do type def and then a already existing type and then you can rename it to a new type and then you can use it as either of the names. So in this, if I just do type def, I don't know, type def int num underscore t, then whenever I actually use a type num underscore t, then it would actually just replace it by an int and then I can go, I can change this type def whenever I want just creates a new name for a type. So I can use this with a struct to make it arguably more readable. So we can create a struct without a name. So I could do something like type def struct and then not give it a name, do the curly brackets, give it a X, which is a double and a Y, which is a double and then I name that unnamed struct point underscore t. So that's going to be my name for a type that's supposed to represent a point. So afterwards I can create a variable with just point underscore t p1 and then I can access it like I could before. I could do p1.x, p1.y, so on and so forth. But first, usually unlike enums you should still give structs a name. So if I want to do this, I could do type def struct point and then give it the curly brackets, give it the X and the Y and then give it the new name point underscore t. And now I have two names I can make it. I can use whatever one I want. I could use struct point. If I don't want to type struct, I can use point underscore t, but both mean the same thing, just have an X and a Y. So if I wanted to create our two points and initialize the fields, I could do something like this instead, which seems like I kind of logically group them here, but seems like a lot of work here. So I could create a point underscore t called p1 and then do p1.x equals one, p1.y equals two. Create another point, I'll call it p2 because I have original ideas and then call it p2.x, or sorry, set p2.x equals to four, p2.y equals to six, and then that looks, that does essentially what I had before, right? But turns out with structures, we can initialize them like arrays. So instead of doing all of that, I can just kind of borrow the same syntax as I have with arrays. So instead to initialize it, I could write point underscore t, p1 equals and then curly brackets. And instead of just setting array values, I'm setting fields in the struct in order. So since my struct had X and then a Y, well, this first one would set X and the second number would set Y. So this is the same as just setting p1.x equals to one, p1.y equals to two, but I can just do it whenever I declare the variable, I can initialize them straight up. So I could also do p2 equals to four, six, and that will be the values just in the order you declare them in the struct. If you want to set the values in some different order, there's a funnier looking syntax. So I can give it a curly bracket and then I can say which field I want to set to what value. So if, I don't know, for some reason, I wanted to set Y first, but Y was the second thing defined in my struct. Well, then I could just do dot Y equals whatever. So I could dot Y equals two, dot X equals one. And yeah, there's a question, how the computer stores these values. So it basically stores these values like an array except that all the elements can have different types. So it does some magic to like figure out where they should actually be in memory. So like arrays, they often be of the same type. For structures, they don't have to be the same type. I could put a bool, whatever, but they're stored more or less like arrays and C gets to keep track of where they actually are in memory, but they'll be in one big chunk of memory altogether. All right, so now I can go back and I could rewrite this code. So I could be like, oh, okay, well I should probably use, so create a struct, so I could do type def struct point, give it a double X, give it a double Y, and then call it point underscore T. So now I have my struct point and I want to rewrite these functions in terms of points. So maybe for this function, what I would do is, well, it takes two points, so I could say point T, P1, just assume that they're like integers or something like that, and then I could point do point P2, and then inside of here, instead of just accessing like X1, X2, well, they're from two distinct points, so instead of X2, that would be P2 dot X, and instead of X1, that would be P1 dot X, and then for the second one, Y2 gets replaced by P2 dot Y, Y1 gets replaced by P1 dot Y, and that should be it. My double, or my print function would be similar. Maybe I want to replace it with point, and then here I would just do point dot X, point dot Y, and then whenever I calculate my distance, so I could create P1, initialize it, so an X and then a Y, create a P2 for, so saving some lines of code, and I'm also grouping things that are logically together. All right, there we go. So doesn't that look a little better? It makes it clear that I'm calculating the distance between two points, and then I could also print out a point if I want. There's no printing one coordinate from one point, one coordinate from another point. They're logically grouped together in this struct, which for this basically it's like an array. So if I go ahead and run that, boom should get 5.0, same thing, but helps me group concepts together. So any questions about these changes here? Yeah, so do I have to write this above all the functions? Yeah, so this I would have to write above all the functions because the C compiler is dumb, it just reads it from top to bottom, and if I use point under score T without telling C what it is, gets really confused. So we can see what that error actually is, so let's just move it down before this function. So the print function knows what point T is, but we'll see here we already have some angry, angry C compiler. So if we do this, it'll probably just say that unknown type name point under score T has to come before everything. But yeah, good to see errors because that is a common one. All right, any other questions? All right, so how could this possibly get better, right? So here's what we had. So it worked, however, no one really writes this because remember, how does C deal with function arguments? So do functions get complete copies of things? Is C copied by value? Well, yes it is. And unlike with arrays that just kind of get turned into pointers, well the same confusingly doesn't happen for structures, they get copied. So function arguments in C again, so this is also review, they are copied by value, and sometimes structures can be very large and it gets a complete copy of them. So even if it is, your structure has like 1,000 variables in it and it is, I don't know, 8,000 bytes large, well each time we use a function there, they get copied by value, they get a new copy of the struct and of course since they're copied by value, if we wrote function that would like modify the X and the Y, well if we modified it in a function we would modify that copy of the struct and not whoever called the function. So we wouldn't get our modifications and also it would be really slow. So in general, it's generally frowned upon to do structs just straight up by copying them by value. And for speed reasons, for usability reasons and normally programmers just always use structures as pointers. So if I want to write my distance function, I would write just a pointer to P1 and then a pointer to P2. So let's see what happens when I do that. So I change these to pointers and then in my main function, well I can't just, just like if I was using scanf or something like that, I would have to give it the address of it, not the actual value because I don't want to copy the entire structure. So I'd give it the address of P1 and the address of P1 and the address of P2. But now when I compile, it does a bunch of things. In fact, I get an error where it says P1 is a pointer. So now how would I actually access the value of P1.x you know, the value of P1.x, now that P1 is a pointer. So think about what I would have to de-reference to get that struct back from the pointer. Yeah. Asterix? Yeah, the de-reference. So instead of writing P2.x, what would I have to write? Yeah. Yeah, so I might have to write star P2.x, but that doesn't quite work, right? Because what it's trying to do here, based off the precedence rules that you don't really have to remember, what this would do is try to take the field of this first. So like try to access P2.x and then de-reference that. So I have to tell the C compiler what I actually mean so I could just put in parentheses, yep. Oh, yeah. Whoops, I said it. Same thing, yeah, we don't know that. Yeah, so if I wanted to do this without anything special, I would have to put parentheses around it. So I could do de-reference P2 and then.x and because of the preference rules, I always have to do parentheses. So does this look nice to do and is this fun to do? So are we having fun right now if I change them all to something like that or does that look like really ugly? Looks pretty damn ugly, right? And it's not even working yet. I'd have to change this one. So we do this and like the parentheses are not optional. I would have to have them. So let's see, make sure we compile and it works. So now it works, but doing that, that looks like one of the ugliest things I have seen since, I don't know, I woke up this morning and saw myself in the mirror. Boom, boom, roasted, all right. So there is actually a special C operator to access fields through a pointer. So if I have a pointer to that structure, well to access it, I would have to de-reference P1, put it in brackets because I need to make sure the precedence rules, like I de-reference it first. So I have to do this, it's kind of a pain. There's actually a new operator that just looks like an arrow. So it is the minus sign and then a less than. It doesn't mean subtract anything and then, or wait, that's greater than. Doesn't mean subtract anything, make it greater than. It's supposed to look like an arrow. So it's supposed to look like it's pointing to something. So that combines de-referencing and field accesses in just one step. So instead of doing parentheses, de-reference P1 and parentheses.x, kind of a pain, I could just do P1 arrow x. So I can change it, I can make it a lot more readable and this is what most people do. So I can just get rid of this. Instead, I could just do P2 because it's a pointer, P2.x, or arrow x. And this one could be P1 arrow x and then P1 arrow y. All right, so I could do that. That looks a lot better, right? Okay, well, if they have to be pointers, it looks a lot better. So if you try to do something like P2.x and P2 is a pointer, your compiler will, so this is probably, once you start doing this, it'll probably be one of the most common compiler errors you'll see. It says P2 is a pointer, did you mean to use this? Did you mean to use the arrow? So it's telling you, instead of the dot, just use the arrow, even points to you at the dot you need to change and need to change it to an arrow. So we just go ahead, change that to an arrow, get ready to see that compiler message a lot of times because typically what will happen is, you might write it the first time and write just point underscore t and then be like, oh no, crap, he told me to make it a pointer and then you change it to a pointer and then you just fix the compiler errors until it actually works. That's generally what ends up happening. So this one is preferred, so this is the preferred thing to do. Take the structures by pointers and then while inside here just use the arrow operator, don't use the dereference in the dot, no one does that. So yeah, we should always use a pointer and to take my own advice, we should change this function as well. So here I just change it back to a pointer. I get red underlines between the dot because the dot needs to change to an arrow. Boom, boom. So now we compile, it works, cool. So questions about that? Okay, so just so you have it in the slides, change the functions to use pointers and we just use that arrow operator and also just so you have it, can access fields with a single dot. If it's a pointer, you'll get this compiler error which you will see probably a bunch of times. Luckily it is an easy fix, just telling you to replace a dot by an arrow. So maybe you decide that, oh, well, I want to create a structure dynamically. And then while you realize that to do that, well, I have to malloc it just as usual. So I would have to malloc and then I don't know how many bytes it takes, but luckily I can ask C. So I could say, hey, C, how many bytes do I need to store a point underscore T? And then I would get back a new pointer that points to that many valid bytes. And then I could just use it as just a pointer. And then, well, because these things are grouped together, I would logically need to set the X to X, the Y to Y. And well, if I have to do that all the time when I have to make one anyways, what usually happens is that you just create your own dedicated function for this. So here I would do something I might call it point create and then it takes in as an argument a double X and a double Y and then it returns a pointer to a new point underscore T. So here I malloc it, I set the two fields and then I return that pointer back. So that lets us create a point dynamically and initialize it in one step. So to create P1, I could just do point underscore T, pointer called P1 is equal to point create one and then two and then as always, if I malloc something, I have to remember to free it. So I would have to remember to free this when I am done. So a questions about that function and what it does. So that should just create a point for us somewhere in the heap. We go ahead and we set its fields to just equal whatever we got from the arguments. All right, so this is typically what you do in Java and stuff like this. This essentially is the thing that gets written for you and why people like Java. So yeah, so this is kind of like a constructor in Java if you have done Java before, but this is C, C is no hand holding so you have to write your own. So usually when we write functions like this or just write functions that just deal with our structure, we just prefix all the functions. So in our case, we would name them all starting with something like point underscore. So I should define the other functions like point underscore distance and then just take the points by pointer. Wow, that's a lot of points in the sentence. And then for the other one, I would just call it point print instead of just print. So this is what my final thing would be. So any questions like that before we get into more fun stuff? All right, so this are things you should follow. I'll preface this by saying this is more advanced C programming stuff. You might not be able to use it for this course. You might not want to use them for this course, but you will see this if you do a C programming or if you have seen other languages but this is the stuff. Anything before this, you definitely have to know. Anything after this is just bonus. So usually what happens, you're kind of encountering this with the recent labs, but usually if once we start writing bigger programs, we usually divide up our code between different files. So we could just write, I don't know, what was this called? This was called distance.c. Well, I started off by calculating distance between two points. Then I created this point structure. Then I did a whole bunch of things using points. Then maybe I make my program, I don't know, do something with graphs or something like that. And then suddenly it gets bigger and bigger and bigger and bigger. And well, usually you want to divide things up to make them more manageable instead of just throwing everything in a single C file. So usually what happens is people will just create a single C file for like all the related functions that operate on a structure. So how code is usually organized is, I might call a file like point.c. And then that file would contain the definition or all the code for point underscore create, point distance and point print. And then point.c could also be the only file that defines the struct itself. We'll see a bit more on this later. This is more advanced usage, but again, do not hesitate to ask questions if I confuse you because this gets fairly subtle. So how we would organize our code, we've kind of used it before like std.io and things like that. So what we would do, we'd create our own header file for these that would have the function prototypes for all of our print functions. So we would create like a point.h that would have all of our function prototypes. And that way we could use these functions in other C files. And remember just a .h that's just called a header file. It's meant to just define structures, define function prototypes and everything like that. So our point.h file would probably look like this which has a few weirder things in it. So it would start with the hashtag, is that what we agreed to call it? So the hashtag, the pound if you're old. So if something starts with the pound, that's something for like the C preprocessor. So what the first two lines here and then the end if at the end do, you don't have to really understand it, but basically what they do is make sure that if we include this file, we only get a single copy of it. So it will check the C preprocessor. You can write like if statements and then define variables. So what this does is says if not defined and then a name. So if a variable is not defined in the C preprocessor called point underscore h, then it defines it. So it creates a new variable called point underscore h. And then it does all of this stuff and then it has an end if. So if I were to write like include point.h twice in a row, well the first time we include it, this variable doesn't exist. So it would define it and then do and then like copy and paste these lines. And then if we included it again, while point.h already exists so it wouldn't actually include all those function prototypes two times because C gets really snarky once you define something two times. It'll say like duplicate definition or duplicate declaration. Please choose one. You should only just give me one. So this is just to make some like C preprocessor magic to make sure that this file only gets copy and pasted once. Don't need to know the internals for that but basically it'll always look something like that. And then in it, we could do type def struct point, point underscore T and here we do not tell it the fields. So we are allowed to just say struct point. We're basically telling C, hey we're going to create a structure called point. I will tell you what fields it has when it is relevant to you. So if you don't need to know, don't worry about it. I'll use it somewhere and the purpose for this is to actually hide the structure. So if you modify it later on, then your code doesn't break in really, really subtle ways which you would experience in other courses which you might experience as software evolves. And then otherwise in our header file we define our function prototypes. So any questions about this header file as it stands? Basically it's telling the C compiler, okay I'm going to create a struct called point. It's also known by point underscore T and I'll go ahead and give you the function prototypes for these three functions. All right, cool. So again that header file, make sure it's only included once or copy and pasted once and it hides the fields of the struct. Anything that starts with a hashtag their preprocessor commands. So that happens before you actually compile it and then C will compile it after all that's done. Just saying struct point and not telling it any fields has a special name that you might see. It's something called a forward declaration which again is just say C, I'm making a struct called point. Trust me, I will define it later so don't worry about it. So why I did this is because well I have to tell C like what a point underscore T is and it doesn't need to know what the fields are to be able to just create a pointer to it. Pointer is just an address to somewhere so I can just use pointers for that struct, no problem. If I got rid of the stars here and I didn't have them then C probably wants to know, oh well if I need to copy this damn structure as an argument, well then I need to know what fields it has, how big it is and everything like that. So in that case it would matter. In the case where you just have pointers, you can do this and it's fine which is the third and most important reason that people typically just use pointers for arguments. So it's just to hide the struct. So if I do this division, then in my main program, this is what I could do. So I could get rid of all of the point functions and just keep it to whatever happens when I start running my program. So all I do is I include point.h, then I have the type point underscore t and I can actually use the functions point create. So here maybe I create a point called p1 and then I print it. Maybe I create a point called p2 again and then print it and then print off the distance between the two points by doing point distance p1 p2 and then after I'm done with them I use point create which did a malloc so I had to make sure that I free them and it would look something like this. So any questions about that? Yeah. So there was some of the difference between like when you add your header file, you add a notation for example std.io we add a kind of reference. Is there a reason for that? Oh yeah, so the question is basically, here let's find it. So basically why is point.h in double quotes and the other ones in angled brackets? So this is a subtle thing. So if I tried to put mine in angled brackets, well turns out probably actually works. Yeah. So still it works no problem. The difference is like really subtle of like where C will look for things for you but the usual practice is usually it doesn't matter and the angled brackets you reserve for things from standard libraries and then use double quotes for anything that you created yourself. So it just lets you know okay this is the thing I wrote this is the thing that's in the C standard library. Turns out it doesn't matter but it's kind of just like a preference thing in like what people usually do. All right, so that's what our main program would be. So now our main program can't access the fields itself which is actually kind of helpful because well you know that the user cannot just change the fields of the struct. Anything that changes the fields of the struct are hopefully in your point.c file. So if something is wrong with it you know exactly what file to go to which really helps once you start developing larger and larger programs. So it's more and it's way more flexible if you can hide the details but maybe you were like oh well I wanted to actually change X or Y and like get the values of X and Y. How do I do that with a pointer especially if I just can't access the fields. So if I go back to my code here and let's say I tried to just use double X equals P1 dot X well because I only have like a forward declaration there whenever we try to compile this it'll be like invalid use of incomplete type def which basically just means well in this header file you just told me that you're creating something called struct point which is a point underscore T. You haven't told me any fields it's not actually complete and if I try to use the fields it just says basically I don't know what the fields are why are you asking for X I don't know what fields this has so don't try it. So that is typically a nice thing to have but maybe I actually want to access X and Y so I need to create more functions and typically you create something called a getter function so that can get the current value of a field and then a setter function that will go ahead and modify the value of a field. So how most real programs will look this would be my complete header file so I would create a function to get the value of X so given a point which I take by pointer I would return a double that represents the current value of X if I want to set it well I don't have to return anything I'm just modifying something so the first argument would be a pointer to the point to modify and then a value to change the current X to and then I would do the same for Y so now I have the full functionality with it but the user can't actually access X and Y directly so questions about that one so I just added some more functions so we can go ahead we can modify the fields if we want or we could get their values so that's our complete header file anything one that uses it doesn't know the definition of the stretch and it's more flexible and we can hide the details so the details would have to be in our separate file so we would create a point.c file like I said before whoops like I said before that actually defines the struct and also has all of the definitions so inside of point.c well I could include point.h turns out I don't really need to but that's usually what you do it's just a style thing and then I would actually define the structure itself so I could do type.struct.point then give it its fields X and Y and then we could have our point create from before we could have our distance from before and then the only difference would be we could have get X so get X would just return the current X value so we just go ahead use that arrow and then to set X well it would just be P arrow X equals X whatever the argument is we get from the function same with Y and then same with print so any questions about that? Cool this is how people actually mostly use structures and mostly like write programs so that they are like logically separate in files and it's much much much much harder to screw things up so here's just the code so you have it in the slides so that was the start of point.h or point.c that was the rest of it and then the rest of it so the only other step we need to do here is when we compile we need to tell the compiler about both files so it needs to compile them both together so that we have all of our code so usually you put your main function in its own file so I might do something and put it in a file named main.c and that has what I showed before just have main and then include point.h and then if I want to compile them and if I want to do it by hand well then I have to tell like GCC if I use as my compiler I give it point.c or main.c and then dash O so that is the name of the executable file and then in this point I use the math library so I have to do dash LM and then afterwards we just run that main executable as before so just to show that there are no tricks or anything here is point.c here is main.c so that just has include point.h really readable that's all it has is 16 lines of code and if I want to compile it well it's just GCC point.c main.c and then the output I want to just call it main maybe I could rename it if I want doesn't matter and then just compile it it would create main and I could just run main as before and here it would print off the first point then the second point and then the distance between the two points. All right any other questions about that? So as thing gets bigger so your lives probably won't get that much bigger but you will see this as soon as you start programming more and more if you read other people's code they're not just going to give you one.c file and then that's it it's going to be divided up into a lot of things and this is how it's how they kind of start to fit all together so with that just remember pulling for you we're all in this together.