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

Поділитися
Вставка
  • Опубліковано 14 жов 2024
  • Patreon ➤ / jacobsorber
    Courses ➤ jacobsorber.th...
    Website ➤ www.jacobsorbe...
    ---
    Working with a Matrix/2D Array in Your C and C++ programs // This video talks about how to declare, allocate, and manage multidimensional arrays, like matrices. Declaring a matrix and passing it to a function has some potential pitfalls. This video will help you be aware of them.
    Related Videos:
    Pointers and arrays: • Arrays, Pointers, and ...
    Double Pointers: • Making Peace with Doub...
    SizeOf Issues: • A common pitfall when ...
    ***
    Welcome! I post videos that help you learn to program and become a more confident software developer. I cover beginner-to-advanced systems topics ranging from network programming, threads, processes, operating systems, embedded systems and others. My goal is to help you get under-the-hood and better understand how computers work and how you can use them to become stronger students and more capable professional developers.
    About me: I'm a computer scientist, electrical engineer, researcher, and teacher. I specialize in embedded systems, mobile computing, sensor networks, and the Internet of Things. I teach systems and networking courses at Clemson University, where I also lead the PERSIST research lab.
    More about me and what I do:
    www.jacobsorbe...
    people.cs.clem...
    persist.cs.clem...
    To Support the Channel:
    like, subscribe, spread the word
    contribute via Patreon --- [ / jacobsorber ]
    Source code is also available to Patreon supporters. --- [jsorber-youtub...]

КОМЕНТАРІ • 84

  • @fmdj
    @fmdj 3 роки тому +56

    Another solution I like and use quite often when I don't know the dimensions of the matrix in advance is to actually just store matrices in one dimension, like int *matrix = new int[rows * cols], then index it with matrix[row * cols + col]. This way the memory is assured to be contiguous and it's easy and efficient to just memcpy the whole array if you need to duplicate it.

    • @rdcarbonk1885
      @rdcarbonk1885 3 роки тому +4

      This is how non sparse matrices are actually handled in general in libraries like eigen c++

    • @gael6252
      @gael6252 3 роки тому +1

      That’s actually really clever

    • @fmdj
      @fmdj Рік тому

      @@doradiv10 no you index it by, for instance for the cell where x = row = 1 and y = col = 2:
      cell
      = matrix[row*cols+col]
      = matrix[1*3 + 2]
      = matrix[5]
      the index will never be out of bounds as your matrix has size (rows*cols) and the max index is:
      (rows-1)*cols+(cols-1)
      = (rows * cols) - 1,
      which corresponds to the last cell of the (rows*cols) 1D array.

    • @fmdj
      @fmdj Рік тому

      @@doradiv10 If I may give you a piece of advice, though you probably know it already: matrix libraries is a solved problem in most environments :)
      Now I still think any developer should develop a Matrices / Vectors library at least once in their lives as an exercise. It's not very complicated but reasonably enough to be interesting.
      I very strongly suggest you to code your Matrix library (should you choose to go down that path) using Test Driven Development because if you choose to roll your own lib it will have to be rock solid and TDD is a good and appropriate method to maximize the chances of that happening.

  • @mrcjproject
    @mrcjproject 3 роки тому +27

    Please correct me if am wrong, is there a bug in the for loop from the main scope, specifically accessing memory which is not yours? MATRIX_ROWS same for both loops

  • @wiktorwektor123
    @wiktorwektor123 3 роки тому +39

    In "main" and "print_matrix" functions of C program (before refactoring) you have serious bug: you're writing and reading (respectivly) 4 bytes (1 position) behind 2 dimensional array. Second for loop should have condition: col < MATRIX_COLS instead of col < MATRIX_ROWS.

    • @hatkidchan_
      @hatkidchan_ 3 роки тому +16

      That thing was bugging me entire video ngl
      I was waiting for it to crash, but oh well

    • @yangliu3754
      @yangliu3754 3 роки тому +1

      not seriously a bug. kinda of a typo.

    • @wiktorwektor123
      @wiktorwektor123 3 роки тому +4

      @@hatkidchan_ It may crash or not, but it will change value of next variable in memory you don't wan't for sure.

    • @wiktorwektor123
      @wiktorwektor123 3 роки тому +7

      @@yangliu3754 Reding and writing memory outside of range isn't typo, but serious bug, you can try to investigate for hours, days or weeks.

    • @yangliu3754
      @yangliu3754 3 роки тому +1

      @@wiktorwektor123 as long as you are happy. you are just missing the point. this is not a video about how to write a typo or even bug free program.

  • @oreo6392
    @oreo6392 2 роки тому +3

    One thing to keep in mind is that in C/C++, "const" doesn't actually mean compile time constant and really just means immutable. (C++ does have constexpr for that, but C, at least at the moment, does not) If you use a "const int" as an array specifier for a variable, it will be a VLA, regardless of if the value can be deduced at compile time. This is different from C++ (which doesn't have VLAs).
    Currently in C the only way to have "MATRIX_ROWS"/"MATRIX_COLS" be a compile time constant is to either use macros or enums.
    Also, for C23, the committee passed a proposal that makes it such that only stack allocated VLAs with automatic storage duration will be optional with all other types of VMTs/VLAs being mandatory. This means VLAs as function parameters and pointers to VLAs will be mandatory. (this also means "sizeof(int[rows][colls])" will be mandatory)

  • @AgnisNeZvers
    @AgnisNeZvers 3 роки тому +16

    I prefer doing 1D way - accessing [x + y * columns]. With that allocation is needed only once. It can also work in more dimensions [x + y * columns + z * columns * rows].

    • @ZeroSleap
      @ZeroSleap 3 роки тому +2

      Yeh this flattened multidimensional array approach is nice.Although a bit hard to read,but really nice

    • @mario7501
      @mario7501 2 роки тому +1

      Yeah in C I tend to do this as well. I think that's how most C++ matrix libraries deal with this as well.

    • @TheRojo387
      @TheRojo387 Рік тому

      @@mario7501 Now, how did I deduce by myself that that was the technique I needed?

  • @ronnysherer
    @ronnysherer 3 роки тому +5

    Good lesson. The next video could be about using one allocation for all ints at once and then just pointing to them. It not only saves many allocations, but it makes sure that the allocated memory is sequential, which might make a performance difference.

  • @SriHarshaChilakapati
    @SriHarshaChilakapati 3 роки тому +2

    I was taught arrays in slightly different way, and that used to confuse me. An array (the subscript syntax) is a syntactic sugar over pointers. So when one is doing int array[5]; and printing it with printf("%d", array[3]); for example, then they are effectively doing *(array + 3) and printing that value. The name given to an array is always a pointer to the first location, that is, the zeroth cell. So array[x] is the same as *(array + x) and it is pointer math.
    Having said that, at 16:57, instead of dynamically allocating the memory, you can still create the matrix as int matrix[MATRIX_ROWS][MATRIX_COLS]; and let the compiler actually reserve the memory for you by allocating the values in stack, and when you need to pass to the function, pass a pointer instead. Then you can pass the matrix address to the function where the signature becomes this:
    void printMatrix(int *matrix, int rows, int cols);
    And when we need to do matrix[y][x], we access it as *(matrix + x + y * cols). However, one thing that bothered me is I wasn't able to access it as matrix[y][x], even if I pass matrix to the function as a doubly pointer. When tried using IDEOne, it crashed with a Segmentation Fault at runtime. Any idea what might be going on?
    My program can be found here: ideone.com/GdWlFk (working version).
    The version that fails: ideone.com/zIg4A7

  • @zrodger2296
    @zrodger2296 3 роки тому +1

    I kind of already knew all this, but only kind of, so this was a great refresher. Like others here I would also like to see how you would do a version where the entire matrix is allocated all at once as an array of int's, and then you manually access them given a row and col. I did this recently in one of my projects.

  • @LogicEu
    @LogicEu 3 роки тому +2

    Hey Jacob, excellent video as always, thank you! Definitely didn't know about those peculiar ways of passing 2D arrays in C functions. You will often encounter another kind of way around this issue, and that is casting everything to a 1D array (as memory is just one string of bytes), and then access it by something like; { array[ row * MATRIX_COLS + col ] = ( row

  • @edgarbonet1
    @edgarbonet1 2 роки тому +1

    Many commenters have suggested flattening the matrix into a 1D array, and indexing it with matrix[row*cols + col], as a flattened matrix is nicer to the cache, and easier to allocate and deallocate. I would like to point out that you can have a flattened matrix and yet keep an array of pointers to rows that lets you have the more convenient syntax matrix[row][col]. This is done as follows:
    int **matrix = calloc(MATRIX_ROWS, sizeof *matrix);
    *matrix = calloc(MATRIX_ROWS * MATRIX_COLS, sizeof **matrix);
    for (int row = 1; row < MATRIX_ROWS; row++)
    matrix[row] = *matrix + row * MATRIX_COLS;

    • @michaelclift6849
      @michaelclift6849 2 роки тому

      And you can also iterate through the entire matrix with a single loop, which is convenient.

  • @yangliu3754
    @yangliu3754 3 роки тому

    Great as always. Happy that I can follow this time.

  • @burlapjack8345
    @burlapjack8345 3 роки тому

    Huh, this video sure was timely. Thanks Jacob.

  • @nickbrian9882
    @nickbrian9882 3 роки тому

    I was literally just looking at this topic yesterday!

  • @AdisDurakovic
    @AdisDurakovic 3 роки тому +1

    Jacob: "I don't wanna confuse anyone"
    .
    .
    .
    Also Jacob: uses

  • @nemis123
    @nemis123 2 роки тому

    Thank you for this video, very informative.

  • @tomatus270389
    @tomatus270389 3 роки тому +25

    I think you missed the method of allocating the whole matrix as a single continuous array and then using pointer math to get to the element.

    • @ayecaptainjack6930
      @ayecaptainjack6930 3 роки тому

      That’s making things a lot more complicate tho

    • @weirddan455
      @weirddan455 3 роки тому +3

      That's probably the way I would do it. You lose a lot of performance with an array of pointers. First, it's a double indirection. Also, it's not very cache friendly. Modern CPUs like things to be stored one right after the other for optimal performance.

    • @pierreabbat6157
      @pierreabbat6157 3 роки тому +1

      That's how I did it in my matrix class, which also handles resizing matrices, adding and multiplying them, Gaussian elimination, and determinants.

    • @VictorRodriguez-zp2do
      @VictorRodriguez-zp2do 3 роки тому

      @@ayecaptainjack6930 I had say it's easier and saves you trouble later.

    • @AIn0n0
      @AIn0n0 3 роки тому

      @@ayecaptainjack6930 in that case allocation of Matrix is just
      malloc(sizeof(MX_TYPE) * MAX_X * MAX_Y).
      Getting n,m element is
      matrix[n + m * MAX_X]
      and you still can make it even easier with inlined functions or macros.

  • @richardbenes9
    @richardbenes9 3 роки тому

    Nice video. If I ever decided to use C/CPP for working with matrices, it would be for performance reasons (and, well, I tend to prefer performance in just about any case).
    This leads me to one, for me interesting, topic - using vectored instructions for number crunching.
    Would it be possible to touch this in some upcoming video?
    How to use them, how to determine whether they are available on that specific machine etc...

  • @mohammadImanmansouri
    @mohammadImanmansouri Рік тому

    Best teacher i love your channel

  • @gaborkiss2206
    @gaborkiss2206 2 роки тому

    Great video as always! I have a question about using 2D arrays like these on embedded system. The bug in the second for loop could cause some serious problems, since the memory is usually limited. How would you debug it? Is it worth to wrap the 2D arrays in a struct or class and add boundary checks on embedded system, or it adds too much overhead? What's your take on this? I know, it depends on a lot of things, but a general idea or some aspects worth to consider would be very helpful.

  • @papaulogamerofc946
    @papaulogamerofc946 3 роки тому +2

    A question: why did you use "col < MATRIX_ROWS" instead of "col < MATRIX_COLS" in both of your for loops?

    • @wiktorwektor123
      @wiktorwektor123 3 роки тому +2

      It's a bug, second loop should be col

    • @JacobSorber
      @JacobSorber  3 роки тому

      Yep, that's called a typo (and a bug). 😫 Thanks for catching it.

    • @papaulogamerofc946
      @papaulogamerofc946 3 роки тому

      @@JacobSorber Oh, OK. Good video anyways. :)

    • @wiktorwektor123
      @wiktorwektor123 3 роки тому

      @@JacobSorber In this case it's bug, not a typo ;)

    • @JacobSorber
      @JacobSorber  3 роки тому +4

      @@wiktorwektor123 It's both.

  • @abhi3142
    @abhi3142 3 місяці тому

    How would the double pointer method work for static or fixed length array (preserving the array indexing)?

  • @TarousDT
    @TarousDT 2 роки тому +1

    I am confused about why you didn't get an access violation/out of bounds error with both nested for loops due to using MATRIX_ROWS with col

  • @rustycherkas8229
    @rustycherkas8229 2 роки тому

    @18:30 (switching to alloc'd buffers) The use of "**" probably scares beginners.
    The alternate equivalent is, of course, int *matrix[] = calloc....
    Even though they are the same, the square bracket version provides a clue (especially in the parameter list of the print function) that the var points to a contiguous array...
    void main( int argc, char *argv[] ) { ......

  • @SlideRSB
    @SlideRSB 2 роки тому

    Flat indexing for the win.

  • @LoesserOf2Evils
    @LoesserOf2Evils 3 роки тому

    Dr. Sorber, what's the difference between a vector and a two-dimensional array? I'd like to see a video on making a class out of this.

  • @ianmathwiz7
    @ianmathwiz7 2 роки тому +2

    3:53 Who else was waiting for him to notice that MATRIX_ROWS in the inner loop should be MATRIX_COLS?

    • @JacobSorber
      @JacobSorber  2 роки тому +2

      I still am. 😀
      Sorry about that. It's things like this that make me wish UA-cam let me update videos. I understand why they don't, but ...😫

    • @dankodnevic3222
      @dankodnevic3222 2 місяці тому +1

      @@JacobSorber LOL, maybe to remake it? Funniest thing was when you COPIED loops... I was sure, at that moment, you will see typo...

  • @yooyo3d
    @yooyo3d 3 роки тому +1

    Did you know... Assume following
    int a[10];
    a[5] = 3;
    This is the same as *(a+5)=3.
    This is also the same as *(5+a) =3.
    So one can write 5[a] = 3 and it should compile and work.
    Similar pointer math is behind matrices as well. You can show people how compiler internally calculate the address of memory location.
    Also rows pointers version of matrix is more flexible in a way that each row can have different number of elements, which is not true matrix... It's more like array of arrays.

  • @afborro
    @afborro 3 роки тому +3

    I'll just say, the C++ version is very old school. Prefer std::array over C style array, or use std::vector. this is why some of us love C++, otherwise one may as well just use C if you are going to new and delete everything.

    • @lean.drocalil
      @lean.drocalil 2 роки тому

      You know, std::array is essentially just a struct on a template. If you don't need the added features of a true C++ Container, there's no feasible reason to demonize primitive arrays. I'm a big fan of C++, but I hardly ever follow the "the C++ way even if just for the hell of it" advices 😁. Regarding vectors, I've had cases (specifically, writing an emulator in C++) in which going with a C-styled array gave me performance, std::vector was introducing too much overhead, even when previously reserved more memory. Standard C is (mostly) standard and valid C++ and when picking a style between the two, I always go with the simplest and most straightforward (I usually printf over std::cout, too).

  • @louiearmandtabelisma9816
    @louiearmandtabelisma9816 2 роки тому

    hi great video is there a way to store output of a counter to a 2d array?

  • @zxuiji
    @zxuiji 2 роки тому

    defining the size of an argument with other arguments, that's a new trick to me, still, I think I'll stick to **mat rather than mat[rows][cols], it's cleaner, more consistent (binary wise) and still simple to understand

  • @RmFrZQ
    @RmFrZQ 3 роки тому

    *Double pointers are matrices\2D arrays.* This makes understanding and *working with* double pointers (ex: **argv) much easier, especially because how C threats pointers in a special way.

    • @italianbeans877
      @italianbeans877 3 роки тому

      Nah that’s not true. Declaring a pointer vs declaring an array are two different things; char *a = “foo” vs char b[] = “bar” behave unlike each other (a cannot be indexed as it points to space in the text area of its process memory, but b can). The confusion arises between arrays and pointers when passing arguments; an array is passed as an arg via a pointer to its first value.

    • @bbq1423
      @bbq1423 3 роки тому

      @@italianbeans877 Both a and b behave the same, you can index a no problem and it will do what you expect. They mean slightly different things though.

  • @vvinoth514
    @vvinoth514 3 роки тому +1

    ❤️ from India

  • @namphantech
    @namphantech 2 роки тому

    do you can explained maxtrix[row][col]=(row

  • @vespert0
    @vespert0 3 роки тому

    Your videos are really nice. Could you focus more on the linking process? And debugging existing real applications? And cross-compiling?
    Where does that "unable to find symbol" error come from? What's __dso_handle anyway? Why is the linker complaining about a library if I'm giving it the correct path in -L and --verbose is showing me it's actually finding it but ignoring it? While your videos are great for begginers, it seems as though you're passively contributing to the college falicy that newly-grads will start working in developing new software, when 99% of the time they'll be supporting existing (often legacy) apps. There are *a lot* of linker errors and debugging issues that make sense to clarify. Just a suggestion.

  • @rogo7330
    @rogo7330 3 роки тому +1

    I prefer 1D arrays with pointers math. It just a more general way to think about data and i don't care how exactly data is structured - just allocate/free/dosmth this much memory and throw it inside function, highlevel code don't care and everything in code looks the same.

  • @ifstatementifstatement2704
    @ifstatementifstatement2704 3 роки тому

    #include

  • @AinurEru
    @AinurEru 2 роки тому

    Alternatively, this works in all compiles for both C and C++ with no need for VLA and no need for allocating arrays on the heap:
    void print_matrix(int rows, int cols, int matrix[]) {
    for (int row = 0; row < rows; row++) {
    printf("%d:\t", row);
    for (int col = 0; col < cols; col++)
    printf("%x ", matrix[row*cols + col]);
    printf("
    ");
    }
    }
    int main() {
    int matrix[MATRIX_ROWS][MATRIX_COLS];
    for (int row = 0; row < 3; row++)
    for (int col = 0; col < 4; col++)
    matrix[row][col] = (row

  • @cmdlp4178
    @cmdlp4178 3 роки тому +5

    In my opinion this nested array allocation is bad for teaching, because it makes beginners think, that as much as possible should be allocated on the heap. I think that it is important for beginners to think about the layout of nested C arrays. The important thing is that is missing here is that such nested C arrays are just simple arrays, that are indexed in a certain way. The index is computed by multiplying the number of colums with the row and adding the column to it. Implementing this on a simple 1D array shouldn't be hard. When trying to make it compatible with C++/any C compiler, this is what is usually used in common apis. It is also the most efficient, because no additional memory is used for pointers and the allocation control structures in the allocator used for allocating all the rows. And also it is efficient, because the access does not have an additional indirection, that comes with the additional pointer dereference, which would mean that it is more cache friendly.

  • @chonchjohnch
    @chonchjohnch 3 роки тому

    For my sudoku program, I used triple pointers to make my columns and cells constant time access lol

  • @Baptistetriple0
    @Baptistetriple0 3 роки тому +2

    why does nobody teach the right way to do 2D arrays in C?
    this is a naive way that have a lot of cons to it, the better way to do an array of size N * M is to do "int (*arr)[M] = malloc(sizeof(int) * N * M);"
    here it is the exact same things as if you had declared a static array like "int arr[N][M];", but it is dynamically allocated,
    you don't need loops to allocate it, and to free it, and you can copy it with just "memcpy(destArr, srcArr, sizeof(int) * N * M);"
    and the speed benefits of having it contigus in memory.

    • @oreo6392
      @oreo6392 2 роки тому

      That's still a VMT.
      In C11 if you want to define __STDC_NO_VLA__, you also can't support VMTs.
      Thankfully that was recently changed for C23.

  • @PavitraGolchha
    @PavitraGolchha 3 роки тому +2

    It really irks me how C++ can't handle 2D arrays.

  • @guilhermefe100
    @guilhermefe100 7 місяців тому

    for(int... this declaration is already too advance for mee

  • @catapl93
    @catapl93 Рік тому

    You're explanations are very chaotic, you don't finish ideas, you keep going back and forth between different ideas and it's just way too much information thrown at once.

  • @caleb7799
    @caleb7799 2 роки тому

    yall keep losing me by going off tangents and showing all the other things we will show later, or something about compiler parts or some other thing or maybe this thing... damn.

  • @windowsxseven
    @windowsxseven 2 роки тому

    I do not trust this ''Jacob''.