Note: I've added a lot of code examples in this article towards the stop that deal with the resize less comfortable problem that I establish difficult to understand. Use a C repl online to copy and paste the github gists (code examples) i've included and past agreement how they piece of work you'll be one step closer to making sense of resize.c!

New format…

Delight comment if you have any questions and i'll practice my best to respond with a detailed answer to help clear anything upward. I'm noticing these problems went from wow that was difficult, to wow what is the significant of life how could anyone solve these problems in a calendar week?!

Padding question

The readme had me stumped on this 1:

What value does line 65 of `copy.c` assign to `padding`  if `bi.biWidth` is `3`?

Here is the code in question:

// determine padding for scanlines int padding = (iv - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

And then broken downwardly:

We're given three for the width and a rgb triple is 3 likewise:  int padding = (iv - (three * 3) % four) % four;  // 3 * 3 = nine  int padding = (4 - (9) % 4) % iv;  // 9 % 4 = 1  int padding = (four - 1) % 4;  // 4 - one = 3  int padding = 3 % 4;  // iii % 4 = iii  int padding = three;              

Does modulo still confuse you? Whenever I step away for a couple of days I lose my "modulo mojo."

Why does 3 % four = 3

because mod gives united states the remainder of a division functioning.

Await at the expression 8 % 4 for case.

Imagine the four hits the 8 and gives 4 damage on the 8. So that 4 can hit 8 twice before there is nothing left.

If the beginning number was ix there would be 1 left. That'southward what mod gives us,whatsoever is left after the 2nd number hits the first number.

So what about three % four?

Well 4 tin't fifty-fifty fight 3 because it can't become at to the lowest degree 4 harm out of information technology. So the fight never happens and modulo gives us what is left in the first number which is the aforementioned as what we started with which is 3!

Whodunit, struct and typedef explained

Although it can be uncomfortable at beginning to wade through someone else's file of code, this was a little easier to solve than terminal weeks helper functions. Remember the lecture speaks about file pointers and modeling our data using structs.

Here is a elementary program to assistance demystify things:

I'll show my answer and explain what is going on (starts on line 80):

In our loop we're streaming in i pixel of RGB at a fourth dimension. To access the value of the electric current pixel we can dive into our struct's backdrop. To access a holding we accept the struct, which in this care istriple, add a dot so our property:

triple.rgbtRed

So we tin can print this value out or run a examination. I wanted to remove all cherry pixels so I created a branch inside the loop between thefread andfwrite functions. This is the perfect place to make modifications because it's right before we write into the new file!

I decided my test would exist if a pixel had any total red value. In hex thats0x0000ff.

If it did I would have that pixel and black it out on all channels. We can do that by taking each property and assigning information technology the value of0x000000, which is blackness.

GDB to the rescue

Working through resize more less comfy correct now. Figured I would requite the more challenging problem a try. Later on watching the video i'grand faced with resize.c and bmp.h feeling very confused every bit to where I should start.

Try this:

  1. make your copy of the file copy.c (I named mine resize.c) and usemake
  2. run the control gdb resize
  3. set a pause betoken afterwards the header variables are created and read into (this was around line 45) using the controlb 45
  4. run the commandr <infile.bmp> <outfile.bmp> (using your own bmp files)
  5. now have a await at where you lot are in the code by using the commandlist
  6. finally impress out either thebf orbistructs using the commandprint bfor print bi

At present yous can see the insides of those variables and what they are made up of when read. Endeavor printing bf.bfSize etc. It makes a huge difference in my comfort level and I didn't accept to write a unmarried print statement!

Understanding File I/O in C

File I/O operations that occur in resize less comfy. We have structs beingness created from a header file, some pointers getting thrown around and what the heck is that nested for loop monster doing at the bottom? Weren't we support to write peachy fiddling functions for everything?

One time y'all understand what is going on in each of these unproblematic programs,write it from scratch yourself. Modify the text, add a conditional if you desire to exist adventurous. Maybe if somewhere there is the character 's' switch it to 'z'. Brand information technology your own to really acquire what is going on. Each time yous runmake and there is an mistake, solve it and be amazed at your growth equally a programmer!

Creating a new file:

Reading in a file:

Visualize file I/O in C

FILE * fp = fopen("stuff.txt", "r");

The FILE data type is known every bit anopaque data blazonso the details are hidden. You should likewise know FILE is a struct which means this creates an object. For now we need to know this is the information type when dealing with files. Simple enough for now…

Next we have the * fp. So nosotros know that fp is a pointer which holds the address where our file lives. Simply what if we don't have a file yet? Well we don't quite have admission to an address space just a "stream." The fp becomes a "file handler."

Nosotros accept for granted how simple it is tosave files on our computer. We don't think for a second about information technology. Back in the twenty-four hour period this was an issue. Programs store data to memory (RAM) while they're running, and if there is enough retentivity, but once stopped (or shut off from power) programs lose everything stored on the computers memory. In order to save information afterwards programs stopped they needed a completely different manner of storing information. Files became the way, and needed a mode to beHANDLED.

Memory (RAM) is commonly a long chip with squares. File storage devices are not all akin. DVD's, SD cards,HDD'south and SSD's are all storage devices that y'all find in modern computers today. But it did not showtime out that way at all!

In the past, data was stored on behemothic spinning disk machines. There were programmers that had to implement code that could save data to these.

Permit's create a pseudo office for that:

saveDataOnSpinningDisk(){   // lots of programming }

It'south 1965 and your managing director comes in and tells yous "They have these new tape machines that store 100 times the data in the same amount of space! we take to write lawmaking to salvage data to these new data storage machines!"

All our programs (recall thousands of lines of code) take a function chosen saveDataOnSpinningDisk().

And then what do we exercise? Maybe we have an if statement like this:

if(using old data storage method){    saveDataOnSpinningDisk() }else{    saveDataOnNewTapeMachine() }

A few years later the HDD (Hard Drive Disk) is invented, and a basement total of tape machines can be stored in the palm of your mitt. Management is telling you to write more code toHANDLE FILES using this new method of storage. Before long CD'due south DVD's Blue-ray discs, SD cards…you go the picture show.

Somewhen people figured out data storage could be abstracted out to their basicoperations. All of these storage devices can existwritten, read, appended to, deleted, and created. Past keeping the specific implementation behind the scenes, our programs only have to deal with an abstraction called aFILE HANDLER, that handles these I/O operations for united states.

The C programming language is taking care of the file I/O specific to our systemfor us. It knows we're on linux ubuntu, on the CS50 cloud server and does the file I/O needed for the organisation. Many digital cameras employ C, which means the aforementioned functions you write to manipulate file I/O tin can be used to shop data on an SD carte du jour. This keeps you focused on the programme at hand, non the distracting details of file I/O.

I finally got through week iii

It really exist similar that though

Honestly,  it didn't accept 8 weeks. It took probably a week of really focused time to complete this trouble. Specifically "less comfortable."

I've created a few elementary programs that helped me understand each part of the program resize.c, so it wouldn't feel and then overwhelming when trying to tackle in i pass.

Not certain how to create a file or make a copy? This little program might help:

Non certain what the original copy.c is doing in the problem set? Endeavor going through my version and meet if it makes more sense. I added a lot of helpful comments.

Things in less comfortable that sucked

I made many attempts just ultimately went on GitHub to observe other programers solutions. My end goal was toempathise the code. Reading it line by line I was able to finally understand what had to be done to complete the claiming. I'll put my solution below and we'll go through each section:

Validating inputs

Upward to line 59 we're but assigning the user inputs to clearly named variables and making sure our file streams are valid and working. On line 22 we have to use the enlargement factor statement every bit an int data type and so using atoi() .

The role on line 51 virtually making sure information technology'southward a 24-bit uncompressed BMP four.0 is a detail we don't have to worry virtually and are just copying from re-create.c.

Update the headers

This covers all the way to line 87. We've got 2 headers, the BITMAPFILEHEADER and the BITMAPINFOHEADER. Read and write in the right guild! I kept bmp.h open in another tab in the cs50 ide to employ as a reference on how the structs are ordered and what information each is holding.

Information technology's best to keep the infile information and the outfile data separate so I created variables for the infile headers (bf and bi) and variables for the outfile headers (obf and obi).

So the video reading less comfortable tells us all the things we accept to update which are:

  • width & height
  • biSizeImage
  • bfSize

Getting the values in this order is key considering each is dependent on the one-time existence updated.

Once all the updates are consummate nosotros employ fwrite() to push that data into our outfile.

After each read and write operation, the infile pointer and outfile pointer is moving forward. Imagine a youtube player and when weread orwrite the footling playhead ball is moving ahead.

The hard part

Starting from line 91 we get caught up in a lot of nested loops.

Get-go we have to call back (whiteboards are very helpful hither) about what we're doing to the RGBTRIPLE in the loop. Allow's depict it out:

before a resize with a resize cistron of 2:

[] []~

[] []~

after a resize:

[] [] [] []~

[] [] [] []~

[] [] [] []~

[] [] [] []~

([] represents one RGBTRIPLE)

(~ is padding and the values can be different)

When nosotros read in a single RGBTRIPLE, nosotros accept to write information technology outn times. Any factor the user gives us. The tough office comes into play when we endeavour to indistinguishable therows.

Notice how the after drawing has 4 rows instead of 2?

I've seen some solutions using fseek by factoring out the 54 bytes of the header and looping through, but it was confusing for me and then I choose to use the malloc() solution.

We store the entire current row of RGBTRIPLE's (pixels) into scanline AFTER we duplicate the number of pixels using a nested loop.

So push themn times into our outfile.

So the inner loop collects what our new row looks similar and the outer loop actually writes to the outfile an entire row at a time.

before:

[][]

after:

[][][][]

scanline takes in an unabridged row [][][][]

then pushes the row into the outfile in the nested loop plus padding cistron times

Support more to line 91. We accept a data type RGBTRIPLE merely it's a pointer type and we're using malloc(). I was hesitant to use this for nigh 2 weeks.

Best fashion to learn is to do with footling programs one without malloc() and one with malloc():

So the above program is without malloc. We declare an array of chars size 3. We know the file is 3Max, iii being our version of a "header" telling u.s.a. the "width" is iii, or 3 chars. If nosotros change the file 3Max to 3JohnnyYzaguirre we aren't going to become the outcome we desire because our variable holds a static number of chars.

We need somethingdynamic, which ways something that can size itself  based on what is required.

Malloc() allocates retentivity by supplying it with a number of bytes. We assign this to a pointer of whatever data type nosotros want to store.

Past having the pointer of a data type (int, char, RGBTRIPLE etc), we can use arrow arithmetic, which allows united states to motion effectually the blocks of <datatype> memory very easily that we have allocated. *WARNING THIS IS Non LIKE A FILE STREAM! THE POINTER DOES NOT Move ON TO THE NEXT AVAILABLE Piece OF MEMORY WHEN YOU READ OR WRITE FROM It!

Try out this programme to run into what I mean. Lots of helpful comments here:

resize less comfy throws a lot at you so i'll requite yous a few hints that I don't feel I completely covered in the article.

  • learn to use fseek() by looking upward the function arguments. Information technology's like a functional way of taking the playhead backwards or frontwards. Y'all merely need to push it forwards in this challenge. Once you lot take the right outpadding you are going to manually add itafterwards you lot've pushed a scanline into the outfile.
  • More on the loop section: The main loop represents the rows of the infile and has 2 inner for loops. the first inner loop represents the individual pixels in a row. Just similar in our example with Max, we accept that temp pixel variable and write it over and overgene times using a nested for loop. the second inner loop is where we bodily write the scanline to the outptr followed by another nested loop where the padding is manually added.
  • The scanline pointer_counter lives inside of the chief loop. This is because we actually demand it to reset to cypher since nosotros're re-using the scanline for the next row from the infile.

Happy coding everyone! Until adjacent time =)