Working with a Matrix/2D Array in Your C and C++ programs.

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey today we're talking about working with matrices or two-dimensional arrays in c and also in c plus plus and we're going to talk about some of the annoying things that you really need to keep in mind when working with them [Music] hey welcome back everybody today we're talking about arrays specifically two-dimensional arrays or sometimes we call them you know called a two-dimensional array of matrix because you can't program very long before you're going to need a two-dimensional array in one of your programs and because they can be a little weird or let's say that there are some things that you need to keep in mind when working with them that sometimes trip people up today's video is definitely going to have source code in it we're going to be creating source code and as always the source code from this channel is available through patreon also that's where you can get access to my discord server and my monthly office hours and all of you wonderful patrons out there who support this channel thank you now let's jump into the code okay so now today we're looking at 2d arrays just to make sure we're on the same page this is an array data structure but instead of just having a single row of data you're looking at something that looks more like a table like this with rows and columns so you have a bunch of different cells basically it's like a table it's like a spreadsheet and those cells can contain just about anything for simplicity today i'm just going to store numbers but they could be strings pointers structs really whatever you want to store in a matrixy sort of way so in this program i'm starting with a pretty empty program here let's just start out by saying by trying to figure out how many rows and columns we want let's define a couple of variables here constants i should say matrix rows and let's just say for let's keep things small we'll make five rows and we'll make columns make a columns constant and let's make that four okay so this is just going to define what size i want my matrix for starters help us avoid magic numbers and then we can come down here and we can just declare our matrix like hit this down in main and say something like int matrix rows and sorry columns and you notice that this is going to be a lot like a 1d array like if it was a 1d array i would just leave this off you know this would just be a one-dimensional array a list of integers but if we want 2 we just add a second set of square brackets like this and so we're basically saying this is the number of rows we want this is the number of columns and this is the type that each of our cells in our little table here is going to be now as we're moving into this you probably want to think about this as an array of arrays okay so the matrix is going to be an array of rows and each row is an array of ants okay so that's pretty straightforward but you want to make sure that that makes sense because all the stuff that follows in this video could get really confusing if that's not really making sense to you okay so now that we have declared this is a local variable that's a matrix now that we have it let's say we want to access an element in the array this is really straightforward we can do stuff like this we can say matrix row 0 column 3 and let's say we want to set that equal to 12. and that's pretty straightforward no surprises nothing annoying and if this was really all there was to it then i wouldn't even have bothered to make a video because this would be really easy but of course we're going to dig a little deeper so let's let's take this a little bit further and let's just let's rather than just setting individual elements of our array let's go ahead and try to set all of them so let's do this with for loops and so we'll just say row equals zero row is less than matrix rows and row plus plus okay so this is basically going to count up it's gonna use a counter to count up by row i'm using row as the name of the counter and then let's just do another one that's going to count up by columns often you're going to see people use i and j for these and you'll see me do that at some point but today i'm just using row and column just so you can make sure you keep track of what we're what we're actually doing with these variables and then because we're going to look at how this is stored in memory i want to actually store something i mean i could do something simple like just say row and column and i could i could just have some number and it is gonna be a number what i'm gonna do here is i actually want to store this encoded in a certain way so that we can look at it later on so what i'm gonna do is i'm going to just take the row number and i'm gonna shift it left by 16 and i'm going to or it with the column number okay now what am i doing here i don't really want to confuse anybody but what i'm doing is i just want the value when we look at it in hexadecimal i want to be able to clearly see the row and column number so you can see how the compiler is laying this out in memory so like i said i don't want this to confuse anybody so let me just explain what i'm doing is the idea here is that let's say that my row variable is 4 and my column variable is 3 then all i'm doing here is shifting the row variable left 16 bits so it's hex representation looks like this and then i order it with my column variable which is three and so the resulting hex value is going to look like this and that's going to be really handy because now if we just look at the matrix in memory we'll be able to see how it's being laid out we'll be able to see what's being stored there and i think that'll be helpful so let me show you what i mean really quick okay so let's save it and let's come down and let's compile our code i am using a very simple make file here let me just i'll show you really quick what this is doing i've got two example programs matrix and matrix two one is in c one's a cpp file and we'll get to that a little bit later but you can see our code compiles just fine and then if i come in here and run this program i've been working on this matrix program well you know it's okay it doesn't do anything it compiles but that's because i didn't tell it to do anything so let's jump back in sorry about that and i will come in here and let's just start out by printing out the size of our matrix because that's that's something i want to look at as we go through here so the size of matrix is going to be we're going to use l u because it is unsigned and it's long and then let's just use the size of operator and get the size of our matrix okay so now let's come back and compile it and we'll run it and okay so you can see we get 80 which this makes sense because each int is 4 bytes and we have 20 of them so 5 by 4 so that's 80 bytes right now i want to show you how things are laid out in memory so i'm going to use lldb my debugger and we'll just run our matrix program in the debugger and i'm just going to set a breakpoint on line 17 right here where my printf is and let's just run it to that point okay whole point is just to get you into memory so we can look at it and now i want to actually look at the memory now like i said i'm doing this in lldb though nearly any debugger is gonna have some way to look at memory and i have a video a pretty old one that shows you how to do this in gdb but in ldb we're gonna do it slightly differently so we're going to use the memory command and read saying we're going to read memory and then we have a couple different options we're going to use dash s which is going to tell us the size of the chunk that we want to look at in this case because i'm looking at ins i want to look at them in chunks of 4 bytes and the format i want to use is hexadecimal that's x so this f option and then the count how many of these integers i want to look at and we want to look at 20. so basically we're looking at 20 integers and then we tell it where to start we want to start with matrix so the address where matrix is located that's what we want to look at and so this is just going to help me see how memory is laid out and you can see here that it shows our addresses over here on the left so this is the address the location in memory where it is located and then our values are over here on the right this is what's actually in memory so it's a lot like a hex editor we've talked about that also in previous videos and hopefully now you can start to see why i use that little encoding scheme because you can see when you look at the first entry here you can see this is row zero column zero this is row zero column one column two column three and then over here you can see the row number goes up to one on each of these so you got one zero one one one two one three so you can just see how it's getting laid out in memory starting at the beginning address and just being laid out in a grid like this although in memory we see it here as a grid because that's how the debugger is laying it out in reality this is just like a big sequence of ones and zeros so this happens to be laid out in a line okay so that's pretty straightforward so far hopefully that's helpful to actually look at it and to see how you can examine things in memory but now what i want to do is to look at what happens if we try to pass this array to a function so let's come up here what i want to do is to make a new function and this function is going to print out our matrix right it's going to print out matrices and let's start by just passing in a matrix i'm going to use the just okay so m a t is gonna be my name and let's just say we would pass in a matrix the size that we've been working with like this okay so i just say hey print matrix you get one argument and it happens to be a matrix with five rows and four columns and this is really a simple case so let's say we just we know what size we're dealing with is the only size matrix we're ever going to be working with so so we can do this okay and now inside we're going to have another i'm just going to copy these another pair of nested for loops one thing when you are working with multi-dimensional arrays uh hopefully you are okay with nested for loops because they're gonna come in a lot but so basically what we're gonna do is we're gonna loop through our array and then right up here at the beginning before we start let's add a printf here this is going to print out the line number so the row number i should say not line um so we're going to print out the row so that way you can see in the output which row we're looking at and let's just just to format things a little better let's add a tab character right there and then each time through for each element we're going to print that out as well and this let's just use percent x because we like hexadecimal and a space and then let's just print our row and column value for our matrix okay now one last thing when we're all done with the row we want to create a new line because otherwise it's going to get all jumbled up but this should be pretty straightforward and then once we have this function we can come down here in main and let's add one more thing let's just say print matrix matrix and that should work okay so but just to make sure let's get out of our debugger down here let's compile it and if we run it you can see sure enough we get the out we get the same output that we saw in the debugger with slightly different formatting basically we're just missing some zeros but the numbers are the same so so this is still all really easy but at this point there are bound to be some complaints so the first thing that people are gonna say is they're gonna be like wait jacob but arguments in c are passed by value right and so does this make a copy of that matrix every time you pass it to the function and isn't that inefficient especially if we were dealing with big matrices and that's a really good point so let's try to address that concern first so i know we just jumped out of our debugger let's jump right back into our debugger and i'm gonna break at main and then i'm also let's see let's break at let's break up here a print matrix so so i want to stop in both places and then we can run this is going to stop us in main now why i wanted to get in here is i just want to look at what's happening i want to try to understand what's happening when we pass this argument and so first of all let's just print out the value of our matrix so this is going to show us actually the well it looks like it tried to give us the contents of the matrix i'm actually taking a moment to figure out what the heck it actually produced but that's not what i wanted to look at anyway we'll have to come back to uldb and figure out what's going on what i wanted to look at here is to print out the address okay so i want you to remember this address keep track of this in your mind this is this we'll just look at the end bff 1d0 this is where our matrix is stored in main okay now if we continue on now we jump into our print matrix and if this were copied would have a different address but let's just do if we print matrix up here there's a couple things that are interesting notice that it didn't try to go through and give me a bunch of elements it just gave me an address and you notice that that address is identical to what there was down in main you notice that the addresses here are the same and so i know that c uses pass by value but in the case of arrays it sort of cheats and treats an array like a pointer which basically gives you pass by reference semantics unless in your mind you remember that arrays and pointers are almost the same thing and i have a video on that if you want to refresh with that link down in the description but if you think about it that way then it makes more sense and we can argue about whether they're cheating or not but we can say that this is still passed by value it's just treating the array as a pointer and passing the value of the pointer and so anyway that's going to be efficient it's going to be fast it's not copying this whole array although the whole array pointer thing may be confusing to some people okay so now let's look at another objection and that is that this code only works for a particular size of matrix like it only works if every matrix that i'm ever working with has the same number of rows and columns and most of us programmers when we are writing code we often want our code to be general i don't want to have a dozen print matrix functions that each handle different size matrices i want just one that i can use for any kind of matrix so what do we do in that case well we do have a few options one option is to pass in a variable length array or vla let me show you how this works is i can add here i can say and rows and int columns and then here i can basically replace this with rows and calls okay and then here i would have to say rows this would be calls okay now with my currency compiler this is gonna work right i pass in the dimensions and then i just say hey i'm also passing in an array that has those same dimensions and this is going to work so if i come down here in main i can change my call a little bit here you got to now pass in the dimensions but now we should be okay now one thing i do want to point out is if you're doing this you need to make sure that the rows and columns basically come first and the reason is is that if i put the matrix first then the compiler is going to complain saying it doesn't know what rows and calls are because they've not yet been defined so we have to define those first in the argument list before we can use them but now this should compile let's just make sure let's come back here and compile it okay and it should run at this point some of you are probably saying cool and others are saying what c can do that and before you get too excited and run off and start converting all of your code to use variable length arrays hold up because i want to just let you know i almost never actually do this and that's because there are a few really annoying things about vlas so the biggest one to me is that since c11 vlas are an optional feature so yeah you heard me correctly compilers don't have to support vlas which of course seems to defeat the purpose in my opinion i mean the whole point of having language standards is so we know what we can expect from any compiler and so while this c compiler handled vla is just fine and your compiler might handle vlas just fine the next one i use or the one you're using might not and that's really annoying now my second complaint is that vlas aren't supported in c plus plus and i know some of you are saying yeah c plus plus does have variable length arrays they're called vectors and you guys that's not the same thing and you know it we're talking about arrays here not vectors but the point is that i if i write this code with vlas then i can't just later come and move it into one of my c plus programs and i find myself doing that often enough that i try to steer clear of things that are significantly different between the languages whenever i think of it so if this is not the way that i usually do it then how should we do it okay well the classic approach it's maybe a little more cumbersome but here let me show you how it works so let's come down here and what we're going to do is let's start by just we're going to take off the columns here and we're going to make this an array of pointers so now what we have is we have an array of rows remember that i said you want to think of a matrix as an array of rows well now that's exactly what we're going to be doing we're going to take advantage of the fact that arrays and pointers are almost the same thing and we're going to make an array of pointers that is as long as the number of rows we have and then for each row we're actually going to allocate that row on the heap something like this and we're going to let's call kellogg and matrix rows and size of int okay so what we're basically just saying is hey i want this number of integers the number of rows and actually i got that wrong it should be calls sorry okay so the number of columns basically as long as each row should be that's the number of columns times the size of int and so that's going to allocate us a new row and then everything else in here can stay the same we might at some point want to check to make sure that calek didn't return null i'm not going to do that today just to keep this example simple but in a real program you might want to check to make sure that you didn't have memory allocation errors they're rare but they're a pain when they come up so now that we've changed our declaration let's also take a look at what's going on in our print matrix function so we can come up here we still need to tell it how many rows and columns but what i'm going to do is change this to a double pointer here and now things should work okay so now we should come back here and we should be able to compile and run and more or less we have the same result except you will notice one thing and that is that our sizeof changed right it used to be 80 now it's 40 why is that well now folks we have if you look down here we have an array of pointers and that array is five elements long and pointers are each eight bytes at least on this machine so an array of five eight byte pointers is going to be 40 bytes total so note that things are being laid out differently in memory so this changes how things are internally like before our matrix was just one block of bytes and that's not the case anymore now the allocator could have given me rows that were all over the place i mean it probably didn't but it could have okay so now i want to also look at one other variant because like one thing that's weird here is this is a local variable with a bunch of heap allocated dynamic memory attached to it so usually what i end up doing um if i want to make this so it doesn't just go away after i'm done here is i might instead i'll leave this just for reference but i might take this off as well make this a double pointer from the beginning and then here what i'm going to do is just call it my my array of rows as well and here we're going to say matrix rows and size of int pointer okay so this is going to allocate for my matrix it's going to allocate an array of rows that is has this many rows and each element is an in pointer so like let's get the size of an end pointer so now my entire matrix is allocated on the heap this might be a little slower because we just added an extra call to calc but for now i'm going to assume that's okay but now the point is is that my whole matrix is on the heap and this comes in handy this is basically how i would do it if i was allocating a matrix that i was then going to return from a function let's say i have a function that's like a create matrix function and i want to create it and then return it to other functions for them to use this is how i would do it because now i can just return the pointer okay now of course any time that we allocate things on the heap we typically want to make sure we're in the habit of cleaning things up okay so what i want to do here is just come down and clean up our memory let's add another loop here and what we're going to do is we're just going to come in here and free each row and then after we're done freeing all the rows we're going to free our matrix array of rows our array of pointers right oh and i said i i want row now keep in mind that order matters here so if you free the matrix first if you were to put this up here at the beginning well the problem is you're freeing all these pointers you're basically releasing them before you try to release what they point to and so your program might crash in unexpected ways if you do this it depends a lot on how your allocator works whether you will crash or not crash but it's not safe so make sure that you free the rows first and then free the matrix okay so this should work but again let's just make sure so we're going to compile it and we're going to run it and it does work except notice again that our size changed once more okay because now our matrix isn't an array of pointers it's a pointer to a pointer and a pointer is just eight bytes and so keep an eye on stuff like that because the size of behavior is changing i have a video on this and how you know size of of a pointer is always eight and it sometimes trips people up but i just want to point that out because this causes a lot of pain and suffering for new programmers okay now as i mentioned before this stuff will work on both c and c plus so let's discuss some minor differences here okay so let me just take this and take this whole thing and we're just going to copy it on top of my matrix 2.cpp file okay so now we are magically over in c plus land now if i'm here and i try to compile it you're going to notice that i get some errors okay so let's just talk about how this is going to work differently over here so one thing that's going to be different is calic so calc returns a void pointer in c because we're used to using mallet calc realic and free void pointers being assigned to some other type of pointer no big deal you don't need a cast in c plus they say uh yeah that's we want to cast okay so here all i have to do is say you know cast this to an in double pointer and cast each of these to an endpointer and we should compile just fine so this is one way to solve this problem the code will run let's just make sure but it runs just fine but some of you are out there saying this is not very idiomatic c plus and you're right it isn't this is kind of the more c style programming that's fine it works perfectly in c plus plus but we could also do this and many in the c plus community would prefer that you do this using new and delete so let's just do that just so you can see how this works is that way we don't need a cast over here we can instead just i'll keep this around just so you can see it but we can instead just come out here and say this is going to be a new int pointer of size matrix rows okay so that's going to take the place of our calc and then down here we can do the same thing here and say new and matrix calls okay so so now we've done that now we of course you don't ever want to mix news and freeze because there's always the possibility that they're doing things differently under the hood when they're allocating the memory so instead here we would want to use the delete operator for our rows we have the square brackets here because we are deleting an array so that just reminds it that we're not just deleting one object that's pointed to we're deleting a bunch a row of them and now this should work here let's just make sure okay so we're in good shape now this is definitely looking more idiomatic all the time we could of course go further and use vectors use standard vector but this is a video about arrays so i'm going to stop here we could make a class to encapsulate this matrix and add bounce checking and handle allocation and cleanup and constructors and destructors and that would actually be kind of nice in this case so let me know if you'd like to see that in a future video but today our time is up like this video if you enjoyed it subscribe to the channel if you don't want to miss the next one if you really like the channel check me out on patreon and my courses and until next week i'll see you later
Info
Channel: Jacob Sorber
Views: 32,780
Rating: undefined out of 5
Keywords: matrix c, matrix in c, 2d array in c, matrix cpp, matrix in cpp, multidimensional arrays, multidimensional array in c, arrays, arrays in c, nested loops, passing arrays as arguments in c, 2d array in cpp, 2D array, programming tutorial, declare matrix in c, Variable length arrays, programming tutorial for beginners, programming tutorial c++, arrays in c language, arrays in cpp
Id: NqEbV5FBKmg
Channel Id: undefined
Length: 23min 39sec (1419 seconds)
Published: Tue Sep 07 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.