When I write labs and projects, it looks like a lot of work. But that’s because I write them like tutorials. This lab has a lot of reading because you are probably new to all of this stuff so I have to explain everything. Read and follow along, and you should be done in no time.


Your computer’s filesystem

Please head over to this page first to learn about your computer’s filesystem. You are no longer a babby CS student.


How you will do your work in this course

Normally, we would have you use a server named thoth to do your work, but uh, the server room kinda flooded last fall. So thoth is offline for a while.

Instead, you will set up a simple virtual machine (VM) on your own laptop in order to have a consistent platform that we will all use. A virtual machine is kinda exactly what it sounds like: an imaginary machine running as a program on your real machine (often called the “host machine”).

I’ve already set up an image, a virtual hard drive, with everything we’ll need. All you need to do is install a program, download the image, and run it.

SSH (secure shell) is a program and protocol that lets you securely connect to a computer across the internet and interact with it.

You know your command-line or terminal interface? Like how you run javac and java? It’s that. Except, instead of your own computer, you’ll be running commands on another computer.


1. Setting up the VM

This cannot be done on a Chromebook. If your primary school computer is still a Chromebook, I’m sorry, but it’s time to upgrade to a real laptop. Please let me know if this is a problem.

qemu is an open-source virtual machine. It’s relatively easy to get installed and running.

Windows Users

  1. Go here and download the qemu-w64-setup-20241220.exe or whatever the exact filename is, the one labeled “QEMU Installer for Windows (64 bit)”.
  2. Run the installer, and just use the defaults for everything (i.e. hit the “Next” button several times).
  3. Now it’s installed, but you can’t run it from the command line easily. You have to “add it to your Path:”
    1. Click Start, type environment variables, and click Edit the system environment variables.
    2. In the top box (User variables for <whoever>), click Path, and then click the Edit… button below the box.
    3. You’ll get a new dialog with a list of paths. Click New, then in the new row, put C:\Program Files\qemu
    4. Click OK to close the paths dialog, and click OK again to close the variables dialog.
  4. Let’s test that it works. Start, type powershell, click it. Then run this command:

     qemu-system-x86_64.exe --version
    
    • You should see QEMU emulator version blah blah etc etc.
    • That means qemu is installed and working!
    • If you instead get an error about it not being recognized, you might have skipped or messed up the environment variable step. Get help.

Now you can scroll down to the Everyone! section below.

Mac Users

You install qemu through something called Homebrew, a package manager for mac.

  1. Run Terminal (⌘+Space, type “terminal”, hit enter)
  2. Type brew and hit enter.
    • If you get “command not found,” you need to install Homebrew. Do so by copying, pasting, and running this command in the terminal:
     /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    
    • It might take a while.
  3. Once homebrew is installed, installing qemu is super easy:

     brew install qemu
    
    • it’ll take a little while again.
  4. Once it’s installed, run this:

     qemu-system-x86_64 --version
    
    • You should see QEMU emulator version blah blah etc etc.
    • That means qemu is installed and working!

Now you can scroll down to the Everyone! section below.

Linux Users

Install qemu however you do for your distro.

Everyone!

  1. Download this compressed image file. Warning, it’s a big one (2.2 GB).
  2. Make a directory for your CS0449 stuff.
    • Windows users, maybe put this directory outside OneDrive. Up to you.
  3. Open the compressed file you downloaded, and drag the cs0449.qcow2 file into that directory.
    • It’ll take a bit.
  4. You can delete the zip file once you’ve done that, since it’s a huge file.
  5. Download the appropriate script below, and put it in the same directory as cs0449.qcow2.
  6. In Terminal/Powershell, cd into that directory.
    • If you ls, you should see both the cs0449.qcow2 and run.bat/sh files.
  7. Run the script:
    • Windows: .\run.bat
    • Mac/Linux:
      • first chmod +x run.sh (you only need to do this once)
      • then, ./run.sh

At this point, a window should pop up, and after a little bit, you should start seeing a ton of text scrolling up it. That’s the VM! It’s booting up!

Eventually, you’ll see this, which means it’s fully booted:

But we won’t be logging into the VM in this window…


A couple more notes on the VM


2. Connecting with SSH

Now you need to connect to the VM so that you can learn to use gcc and other commands. ssh is the command you use on your host computer to connect to the VM.

  1. Open another tab or window in Terminal/PowerShell.
  2. Run ssh student@localhost -p 10022
  3. Say “yes” to the “authenticity of the host cannot be established” prompt
    • you only need to do it this first time; it will be remembered from now on
  4. For the password, type cs0449
    • Nothing will show up when you type your password. That’s normal. Just type it and hit enter!
  5. You’re in! You should see a command prompt:


3. Where are we?

When you log in, you are placed in the home directory. The home directory is where all your personal files are. The same concept applies to Windows and macOS too.

  1. Try using pwd; it’ll show you the full path of your home directory, /home/student.
    • Your home directory can also be referred to as ~ in many commands as a typing shortcut. (That’s what the ~ in the prompt means.)
  2. Try using ls. It will list the files and directories.
    • The private directory is where you will do your work. Technically no one else can see your stuff in this VM, but it’s a good habit to use this directory if/when we get thoth back.
    • Do ls -a and you’ll see a lot more files and directories.
      • The convention is names that start with a period . are hidden by default.
      • The -a option to ls means “list all things”.
  3. Do cd private and you’ll move into that directory.
    • pwd again, and you’ll see that your directory changed.
    • You can use cd .. to move up one directory.
    • You can use cd ~ to go to your home directory.

Common UNIX commands

Here’s a quick reference guide to refer back to.


4. Getting some examples and using gcc

Let’s learn how to use gcc by getting and compiling some of the examples I’ve given you. Refer to the commands above to follow these instructions.

  1. Go to your private directory.
  2. Make a new directory called cs0449, and go into it.
  3. Make a new directory called examples, and go into it.
    • At this point, the current directory should end in private/cs0449/examples.
  4. Open the course materials page.
  5. Right-click on 1_hello_world.c and choose “Copy link” or similar.
  6. In your terminal connected to the VM, type wget, a space, and then paste the link you copied:
    • Windows PowerShell: hit Ctrl+V.
    • macOS Terminal: hit ⌘V.
  7. At this point you should have this command ready to execute:
     wget https://jarrettbillingsley.github.io/teaching/classes/cs0449/examples/1_hello_world.c
    

    Hit enter to execute it.

    • wget is a command that downloads a file from the internet. In this case, we are telling your VM to download a file from my site directly into your AFS space. Very convenient!
    • If you get the error wgethttps://............c: No such file or directory, you didn’t hit space after typing wget 🙃
  8. Use wget to get the 1_cant_add_strings.c and 2_get_line_success.c examples too.

If you ls you should see the three .c files you just downloaded. Try looking at their contents with either cat or less!

Now let’s compile.

  1. Type gcc 1_hello_world.c. Wait, before you type that…
    • Type gcc 1_he. Then hit the tab key. It will fill in the rest of the filename for you.
    • This is called tab completion and it is everywhere in programmer-oriented tools and it saves a lot of time.
    • It works on your computer too. No more typing java MyReallyLongClassName.java or cd MyReallyLongFolderName.
  2. Hit enter.
    • If you did it right, it should print nothing. With UNIX, “no news is good news.” Successful commands will usually be quiet. But if you ls, you should now see a new file, a.out. This is the executable that gcc created!
  3. Run the program by doing ./a.out (yes, you have to type a period then a slash before it)
    • It should print “Hello, world!”
  4. Repeat for the other two examples you downloaded.
    • Every time you compile, gcc will replace a.out with the new executable. This might not be what you want. If you want to name the output executable something other than a.out, use the -o option to gcc:
       gcc -o 1_hello_world 1_hello_world.c
      

      The thing you put immediately after -o will be the executable’s filename.

      • Executables on UNIX typically have no file extension, so 1_hello_world is a perfectly normal name.

5. Making your own program

  1. Change to your ~/private/cs0449 directory, then make a labs directory and go into it.
  2. Run nano username_lab1.c, where username is your username. My username is jfb42, so for me, but NOT for you, oh my god, put YOUR username, not MINE:
    • jfb42_lab1.c - the one and only acceptable filename.
    • JFB42_lab1.c - uppercase is bad. your username is lowercase.
    • jfb42_lab01.c - it’s lab1, not lab01
    • jfb42_rec1.c - it’s lab1, not rec1
    • jfb42_lab1 - no extension
    • jfb_lab1.c - incomplete username
    • jarrett_lab1.c - that’s not a username
    • lab1.c - no username
    • ❌ literally anything other than the first thing on this list
    • Please follow my lead. It’s not that hard.
  3. nano is a simple text/code editor that runs in the terminal.
    • At the bottom you will see the key shortcuts listed in a strange way.
      • ^O means Ctrl+O. (macOS users, this means the key labeled control, not command!)
      • M-U means Alt+U. (M is a reference to the meta key from long ago.)
        • macOS Terminal users: make sure Edit > Use Option as Meta Key is checked. Then you can use Option+U for M-U.
    • Besides the strange key shortcuts (Ctrl+O saves the file instead of opening one? Cut and paste are Ctrl+K and Ctrl+U???), nano works pretty much like any other text editor.
    • You may be able to scroll with your trackpad/scroll wheel, or you may be forced to use arrow keys and page up/page down.
  4. Type, don’t copy-and-paste, this code into nano:

     // Fake Studentname (abc123)
     #include <stdio.h>
    
     int main() {
         printf("Hello World!\n");
         return 0;
     }
    
    • the first line of the file should be a comment containing your full name and username in the format shown above.
  5. You can actually save with Ctrl+S. It should say something like [Wrote 7 lines] at the bottom.
  6. Exit with Ctrl+X.
  7. Compile and run that file.

And there you go.


If you don’t want to have to exit nano

You can ssh into the VM multiple times simultaneously!

Make another tab/window in your Terminal/PowerShell and ssh into the VM like you did before. cd into your ~/private/cs0449/labs directory. Now you can use nano in one tab, then switch to the other to compile and run. Definitely more convenient.


6. Taking it further

Instead of just being a “Hello world” program, you are going to make your program take some text input, transform it, and then print out the transformed text. When you run your program, it should work like this (the $ lines are the command prompt, you don’t type those):

$ ./a.out
Type something in: this is what the user typed in!
Now in uppercase: THIS IS WHAT THE USER TYPED IN!
$ _

That is, it:

  1. asks for a line of text
  2. reads a line of text
  3. converts it to uppercase
  4. prints it back out in uppercase
  5. <that’s it, it’s done, it doesn’t loop, it just exits after one line of text>

So here’s how to accomplish that:

  1. Edit your lab in nano. Remove the printf("Hello, world!\n"); line from main.
  2. Open the 2_get_line_success.c example file on your computer (like, download it from my site and open it in your code editor), copy the get_line function out of it, and paste it into your lab in nano above main.
    • You can really just hit Ctrl+V/⌘+V to paste the code into nano. Your terminal program handles it.
  3. At the top of the program, add #include <string.h> so that you can use strlen().
  4. Write a function to uppercase a string. Name it uppercase. Here’s how you will implement it:
    • C has a function toupper() in <ctype.h>, which converts a single character to uppercase.
    • Your uppercase function will take a char* argument that is the string to uppercase.
    • It will uppercase the string in-place.
      • (That is, it will change the values in the string, not make a new one.)
    • Remember when writing your loop that that strlen() is a O(n) function!
      • You do NOT want to call it once per loop. Otherwise, your loop will be O(n^2).
      • Put the string’s length into a variable before the loop, and use that in your loop condition.
  5. Now in main, use get_line() and uppercase() to get a line of input, uppercase it, and print it out so that your program behaves like the demonstration above.
    • Make the input buffer 200 characters long, and pass 200 to get_line to tell it how long the input buffer is.
    • Follow the lead of 2_get_line_success.c to call get_line properly.
  6. Test your program and make sure it works correctly with:
    • letters (lowercase letters should become uppercase; uppercase letters should remain the same)
    • numbers (should remain the same)
    • punctuation (should remain the same)

Before you submit this, let’s take a little break to learn about diagnosing segmentation faults.


7. Finding out where a segfault happened

“Segfault” is short for “segmentation fault” and is one of the more common ways for C programs to crash. C has no exceptions and cannot tell you where the program crashed or for what reason (out-of-bounds index? null pointer? stack overflow? who knows!). So, we must use a debugger to help diagnose the problem.

A debugger is a program that monitors your program as it runs, and lets you do things like:

You’ll be getting much more practice with the debugger in a later lab, but for now, you need to learn how to debug a segfault.

Making a program that segfaults

  1. In your VM terminal, go into your ~/private/cs0449/examples directory.
  2. Use nano to edit 1_cant_add_strings.c. Change x from 5 to 1000000.
  3. Save and exit. Do gcc 1_cant_add_strings.c and ignore the warnings.
  4. Do ./a.out. It says Segmentation fault (core dumped). There we go!

Finding out where it happened

In order to find where the segfault happened, we actually need to recompile the program, but we need to tell gcc to include debugging information. This is crucial, because by default the compilation process is “lossy” - all the information about which machine code instructions correspond to which lines of code in the original program is lost, as are variable names and types etc.

  1. Recompile the program with gcc -g 1_cant_add_strings.c. That is, add the -g flag.
  2. If you run ./a.out again, nothing has changed, it still segfaults. However…
  3. Now you can run gdb ./a.out.
    • gdb is the GNU Debugger.

It will say a bunch of stuff, and then say:

Reading symbols from ./a.out...
(gdb)

This is now waiting for you to type a command.

  1. Type the run command and hit enter to start running the program.
  2. You will see a message like:
     Program received signal SIGSEGV, Segmentation fault.
     __strchrnul_sse2 () at ../sysdeps/x86_64/multiarch/../strchr.S:32
    

    Segmentation fault! There it is! But this time gdb caught it in the act.

  3. Type the where command and hit enter.
    • This prints out a stack trace that shows all in-progress functions at the time the segfault occurred.
    • Entries #0, #1, #2, and #3 are all inside the standard library, and aren’t very useful, but…
    • Entry #4 says in main () at 1_cant_add_strings.c:8 - there it is! That’s where, in your code, the segfault is occurring!

At this point, you can use the quit command to exit gdb (say y to kill the program).

To summarize, when you get a segfault:

  1. Compile your program with -g
  2. gdb ./programname
  3. run
  4. When it crashes, where, and look at the first entry that is inside your code to find the culprit line.

8. Downloading with scp

Alright, you’re now ready to submit. You will submit your code to Gradescope on Canvas, but right now, your code is still in the VM. To get it out of the VM, we can use the scp (secure copy) command, which kind of does a cp over an ssh connection:

  1. On your computer, you need to open a second shell window:
    • Windows PowerShell: Ctrl+Shift+T to open a new tab
    • macOS Terminal: ⌘+N to open a new tab
  2. In that new terminal, cd to the directory where you want to download your lab
  3. Then run this, but with your username instead of username:
     scp -P 10022 student@localhost:private/cs0449/labs/username_lab1.c .
    
    • notice the space and period at the end! that is crucial!
  4. It will ask for the password like when you used ssh; type cs0449 and hit enter
  5. If you typed the filename correctly, it will download the file to the current directory.

Submitting to Gradescope

The gradescope isn’t up yet. It will be soon.

Now your lab is downloaded to your computer, and you can submit it to gradescope:

  1. Go to the canvas for this course.
  2. Click Gradescope on the left.
  3. Click on Lab 1.
  4. Upload your .c file.
  5. Wait for the autograder to run, and it will tell you if there were any issues.

If the autograder says something is wrong with your submission, fix it in the VM, re-download it to your host computer with scp, and re-submit it as many times as needed to fix the problem.

Note that future assignments may have a limited number (or rate) of resubmissions, so do not rely on the autograder to be your only method of finding and fixing problems!