Submind YouTube summaries
Thumbnail for "Structures" Introductory C Programming for ECE at University of Toronto

"Structures" Introductory C Programming for ECE at University of Toronto

Watch on YouTube

Video summary

The lecture introduces the concept of structures in C, which allow programmers to group related variables into a single new type called a struct. The primary motivation for using structures is to improve code readability and reduce errors by logically grouping data that belongs together, such as the x and y coordinates of a point. Instead of passing individual variables like x1, y1, x2, and y2 to functions, developers can define a struct named "point" containing these fields and pass entire points as arguments. This approach makes it immediately clear what data is being manipulated, preventing confusion where one might accidentally mix coordinates from different points. Furthermore, structures can be initialized efficiently using array-like syntax when declaring variables, allowing all fields to be set in a single line based on their declaration order within the struct definition. However, passing structures by value is often inefficient and problematic because C uses copy-by-value semantics for function arguments. When a large structure is passed to a function, the entire data block must be copied into memory, which can be slow and unnecessary if the function only needs to read or modify specific parts of the data without altering the original. To address this performance issue and avoid unintended side effects where modifications inside a function do not reflect in the caller's scope, it is standard practice to pass structures as pointers. When using pointers, accessing the fields requires dereferencing the pointer, which can be verbose and prone to precedence errors if parentheses are omitted. C provides a specialized arrow operator (->) that combines dereferencing and field access into a single, cleaner syntax, making code significantly more readable than manually writing asterisks and dots. To manage complexity in larger programs, the lecture emphasizes organizing code across multiple files using header files and source files. A header file typically contains forward declarations of structures and function prototypes without defining the actual fields, while the corresponding source file defines the complete structure and implements the functions. This separation allows developers to hide implementation details from other parts of the program; users only need to include the header file to use the functions without knowing the internal layout of the struct. If a user attempts to access hidden fields directly in their code, the compiler will reject it because the type is incomplete in that context. Additionally, the lecture demonstrates how to create custom constructor-like functions using `malloc` to dynamically allocate memory for structures and initialize them, ensuring proper resource management by requiring corresponding `free` calls to prevent memory leaks. Finally, the course teaches best practices for encapsulating structure data by providing getter and setter functions rather than exposing fields directly. By defining separate functions to retrieve or modify specific values like x and y, programmers maintain control over how data is accessed and modified, which enhances code safety and flexibility. The lecture concludes by showing how to compile multiple source files together using a command-line compiler, linking the main program with the structure definition file and necessary libraries. This modular approach scales well as programs grow in size, allowing developers to manage complex systems by dividing logic into distinct, manageable units that can be compiled and linked efficiently, forming the foundation for robust C programming projects.
Read the full video transcript
[Music] very all righty 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 uh 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 within 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 a 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 En num short for an enumeration well you define a struct just below the includes and typically you do not do it within a function so we will spend the rest of the leure 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 po you know the difference and then 2.0 so that will just Square it and then plus uh 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 that so I just give it to the square root function which of course they called squir 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 1 y1 equal to 2 X2 = to 4 Y2 = to 6 and then I could calculate the distance and then we can review we can go ahead just print it to one decimal place because it is a double value so if I go ahead and run this I 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 going to make it much much better uh 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 well 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 make as many variable DEC 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 Bo whatever so you can create a variable with it but instead of just saying it's type is one of the things we've already learned you could create a new variable by saying struct point and then 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 um 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 doy 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 en nums so the syntax of a type def just in case we missed that one on Friday is you can type 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 I don't know type def int numor T then whenever I actually use a type numor 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 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 pointcore T so that is going to be my name for a type that's supposed to represent a point so afterwards I can create a variable with just pointcore T P1 and then I can access it like I could before I could do p1.x p1y so on and so forth but for usually unlike enums you should still give strs 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 point and then give it the new name pointcore 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 pointcore 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 grouped them here but seems like a lot of work here so I could create a pointcore t called P1 and then do p1.x = 1 p1. Y = 2 create another point I'll call it P2 because I have original ideas and then call it p2x or sorry set p2x equals to 4 p2y equals to 6 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 X as I have with arrays so instead to initialize it I could write pointcore 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 equal to 1 P1 y equal to 2 but I can just do it whenever I declare the variable I can initialize them straight straight up so I could also do P2 equals to 4 6 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 doy equals whatever so I could do yal 2 doxal 1 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 have to be of the same type for structures they don't have to be the same type I could put buol whatever they're but they're stored more or less like AR raay and C gets to keep track of where they actually are in memory but they'll be in one big chunk of memory Al together 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 pointcore 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 dox and instead of X1 that would be p p1.x and then for the second one Y2 gets replaced by p2y y1 gets replaced by P1 doy 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 just do point dox point doy 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 between two points and then I could also print off the value or just print out a point if I want there's no like printing one coordinate from one point one coordinate from another point they're logically grouped together in this struct which uh 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 to together so any questions about these changes here yeah uh so do I have to write this above all the functions yeah so this I would have to write above all the functions because you know the C compiler is dumb it just reads it from top to bottom and if I use pointcore 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 will probably just say that unknown type name pointor 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 copy 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 copy 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 a thousand 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 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 strs just straight up by copying them by value um and for Speed reasons for usability reasons and normally programmers just always use structures as pointers so 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 while 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 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 you know the value of p1.x now that P1 is a pointer so think about what I would have to dreference to get that struct back from the pointer yeah asteris yeah the D reference so instead of writing P 2.x what would I have to write yeah yeah so I might have to write star p2x 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 dox and then D reference that so I have to tell the CM compiler what I actually mean so I could just put in parentheses yeah 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 D reference P2 and thenx 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 the m 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 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 oh don't know I woke up this morning and saw myself in the mirror ha boom boom roasted all right so there's 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 dreference P1 put it in put it in Brackets because I need to make sure the Precedence rules like I 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 comines D referencing and field accesses in just one step so instead of doing you know parentheses D reference P1 and parentheses dox 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 that this is what most people do so I can just get rid of this and instead I could just do P2 because it's a pointer P2 dox 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 dox and P2 is a pointer your compiler will so this is probably once you start doing this this will 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 would just go ahead change that to an error 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 pointcore T and then be like oh no crap he told me make it a pointer and then you change it to a pointer and then you just fix the compiler errors until it actually works it's generally what ends up happening so this one is preferred so this is the preferred thing to to do take the structures by pointers and then well inside here just use the arrow operator don't use the D reference 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 you know 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 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 it's 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 well you realize that to do that well I have to Mal loock it just as usual so I would have to Mal loock and then I don't know how many btes it takes but luckily I can ask C so I could say hey C how many bytes do I need to store a pointcore 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 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 pointcore T so here I Mal loock 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 uh pointcore T pointer called P1 is equal to point create one and then two and then as always if I Malo something I have to remember to free it so I would have to remember to free this when I am done so any 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 uh 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 handholding 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 uh pointer wow that's a lot of points in a 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 by saying this is more advanced C programming stuff you might not be able to use it for this course you might shouldn't you might not want to use them for this course but uh you will see this if you do any 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 rec Labs but usually if once we start writing bigger programs we usually divide up our code between different files so we could just write you know I don't know what what was this called this was called distance. C well I started off by calculating the 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 fun all the related functions that operate on a structure so you 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 pointor 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 way this is more advanced usage but again uh 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. and things like that so what we would do we' 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. file would probably look like this which has a few weirder things in it so it would start with you know the hashtag is that what we agreed to call it so the hashtag the pound if you're old so if it something starts with the pound that's something for like the C pre-processor so what the 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 croo pr preprocessor you can write like if statements and then Define variables so what this does it says if not defined and then a name so if some if a variable is not defined in the C preprocessor called pointcore H then it defines it so it creates a new variable called pointcore 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 well 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 m c pre-processor magic to make 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 pointcore T and here we do not tell it the fields so we are allowed to just say uh struct point we're basically telling C hey we're going to create a structure called point I will tell you what f 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 you know your code doesn't break in really really subtle ways which you would experience in other courses which you might experience uh as software evolves okay and then otherwise in our header file we Define 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 it's also known by pointcore 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 one once or copy and pasted once and it hides the fields of the struct anything that starts with a hashtag their pre-processor commands so that happens before you actually compile it and then C will compile it after all that's done and just saying struct point and not telling it any Fields is has a special name that you might see it's something called a forward declaration which again is just say see 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 pointcore 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 pointcore 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 malok so I had to make sure that I free them and it would look something like this so any questions about that yeah what's the M difference between when you add addon 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 um 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 just lets you know okay this is the thing I wrote this is the thing that's in the C standard Library turns out doesn't matter but it's kind of just like a preference thing and like what people usually do um 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 just change the fields of the struct any thing 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 try to just use double x equals p1.x well because I only have like a forward declaration there whenever we try to compile this it'll be like in valid 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 pointcore t you haven't told me any Fields it's not actually complete and if I Tred to use the fields it be 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 would 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 value 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 X2 and then I would do the same for y so now I have the full functionality with it but the user can actually like 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 we any one that uses it doesn't know the definition of the stretch more 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 Doh 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 typ def 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 git X so git 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 separated in files and it's much much much much harder to screw things up um so here's just the code so you have it uh 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 name main. 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 that as my compiler I give it point. C point or main.c and then - 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- 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 wanted 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 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 Labs probably won't get that much bigger but you will see this 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 uh just remember pulling for you we're alling this together