COS 371: Programming Languages

Spring 2022

hw6: C lab, part 1

Due: Wednesday, 03/02 at 22:00

This assignment is to be done individually. You can share ideas and thoughts with other people in the class, but you should code your own assignment.

1. Goals

To become familiar with some basic notions in C. To get used to using Git and GitHub.

2. Introduction

All C programming for this course should be done on Linux. You may SSH into our department server wiebe2.mathcs.bethel.edu if you do not run Linux yourself. On a Windows computer, you may use PuTTY to connect. On a Mac computer, you should be able to type

ssh wiebe2.mathcs.bethel.edu
directly on a teriminal.

If you get error "This account is currently not available."

If you would like to develop C code on a non-Linux machine, do so at your own risk. (One way is to use Windows Subsystem for Linux.) We will compile your code on Linux when grading, and if it doesn't work under Linux, points will be deducted.

Part of the reason why we are using Linux is that later you will use a tool called Valgrind. Valgrind is not available on Windows, and its Mac version is buggy. I do not know of a free equivalent software for Windows. They tend to cost a lot of money.

3. Getting started with Git and GitHub

Before getting started with C programming, get set up with GitHub to submit your code. Do the following:

  1. Go to the GitHub organization page for this course. (You can accept the invitation to join the organization if you have not.) You should see a repository named username-lab, where username is your username. (Similarly, replace any occurrence of username below with your own username.) Contact me straight away if the repository isn't there.
  2. Clone the repository. In your web browser, click on the repository titled username-lab. This should take you to the home page for that repository. Find the box on the page that shows you the link for your repository, and make sure "SSH" is selected instead of "HTTPS". Copy the URL.

    In a terminal on the Linux machine, navigate to an appropriate directory that you wish your repository to live in. Then type git clone URL where URL is the URL you copied from above. It will probably look something like this:

    git clone git@github.com:cos371-22s/username-lab.git
    
    This command will likely fail, we'll fix that next.
  3. Setup GitHub authentication. If cloning worked, you already have GitHub authentication set up, so you should skip this step. Otherwise, follow these instructions. When you are done, try the git clone command again, and it should work. If not, troubleshoot or get help.

    (Note: there are some alternatives, including using HTTPS and a personal access token. I am recommending the SSH key approach because I find it to be by far the most convenient in the long run.)

  4. Add a file to the repository. To make sure that Git and GitHub are up and running, create a file and add it to the repository as follows.

    If everything worked, you should have received a (seemingly) empty directory (probably named username-lab). Please cd into it:

    cd username-lab

    Create a credits.txt file and write "Author: YourName" with "YourName" replaced by your name.

    Then issue the following commands:

    git status
    git add credits.txt
    git status
    git commit -am "Added a credits.txt file."
    git status
    
    The first status command should show you that you have a new file that is not yet tracked by Git. The add command will add it for tracking. The second status should now show that you have a file with changes ready to be committed. The commit command will commit the changes to your local repository. The string in quotes is the commit message, which should be a short description of what changes you are committing. The third status should show you that the commit worked.

    Of course, you may skip the git status commands.

    Note: If this is your first time using Git, you may receive a warning that your name and email address were configured automatically. To make that message go away in the future, enter in the two Git config messages that it will show you.

  5. Push changes to GitHub. The commands in the previous step were operating locally, without using the Internet. Once you have successfully added your file and committed it to the local repository, push it to GitHub by issuing the following command:
    git push
    
    This tells Git to push your changes to GitHub.

    Note: If this is your first time pushing, you might get a warning message claiming that "push.default is unset." If so, follow the recommendation it provides to "adopt the new behavior now."

    git config --global push.default simple
    
    Then try again:
    git push
    

    Note: The first time pushing a repository may require

    git push origin main
    
    instead. You should only need to add origin main the first time.
  6. Go back to GitHub in your browser. Within your repository, click on the credits.txt file to confirm that your edits are showing up there. Hopefully the above all works as expected; if not, ask for help (from me, a classmate, or a CS lab assistant) before going further.

4. Getting started with C

When doing the exercise below, update credits.txt by listing anyone who helped you with the assignment (including lab assistants and classmates), and any websites, books or other sources you used for reference. Also briefly describe how you were helped. Please try to be thorough. If you didn't get any help, you could write something like "I worked alone."

  1. Compiling and running your first program

    You'll need a text editor and a C compiler to work. I recommend vim for text editing. You must use clang for the C compiler (not gcc).

    In your local Git repository directory, create a file hello.c with the following contents:

    /* A first C program. */
    
    #include <stdio.h>
    
    int main() {
       printf("Hello, world!\n");
    }
    

    Add the new file to the repository:

    git status
    git add hello.c
    git status
    git commit -am "Adding hello.c"
    git status
    

    Compile the program to a binary executable by running the following in the terminal:

    clang -o hello hello.c
    

    This command should produce a file name hello. Try running it.

    $ ./hello
    Hello, world!
    
    The dollar sign ($) above represents the shell prompt. It should not be typed in. This is a (common) typographical convention to help distinguish between what to type in and the program output.

    EXERCISE: Change the program to print "Hello, <your name>!" instead. You will have to recompile the program before running it again. When complete, add and commit your changes with Git, and then push your changes to GitHub. Verify that you can see your updates on the GitHub website.

    I prefer that you add and commit your source files (*.c) only: do not include the *.o files or the compiled binary hello. If you added them, it is not a big deal; you don't have to remove them.

  2. Printing

    The first non-commented, non-blank line of hello.c is: #include <stdio.h>. This directs the C compiler (or technically, a subprocess called the C preprocessor) to include a header file named stdio.h that lists the signatures of standard input and output functions, such as printf.

    In a terminal window, type man 3 printf. All of the functions in the standard C library are documented in the system manual. (The 3 is because there's also a bash command named printf, so you have to specify which manual to use.) The man page tells you which header file to include to get access to that function (in this case, stdio.h), as well as documenting the interface and behaviour of the function. In this case, the man page also includes several similarly-named functions such as sprintf and fprintf. Read through the man page for printf.

    The printf function is similar to Python's string formatting operator or Java's System.out.format in that it accepts a string that includes format specifiers to print integers, floats, etc. Download the file printing.c, which contains a number of different examples of print formats.

    Here are common C types and their printf formatting specifications:

    Format specifier Type
    %c char
    %i or %d int
    %li or %ld long
    %f float or double
    %s string (really a char *)
    %p pointer (e.g. int *)
    There is nothing to turn in.
  3. User Input

    You can get user input using the scanf function (see man scanf).

    int i;
    scanf("%d", &i);
    printf("You entered: %d\n", i);
    

    The first argument to scanf is a string containing formatting specification(s) for the type of input expected. In this case we expect an int.

    The second argument should look pretty weird to you. How is scanf able to modify the value of i? What's that ampersand (&) symbol? The answer is that we're actually passing a pointer to i's location, not i itself, so that the value of i can be modified from within scanf. We will discuss much more about pointers soon.

    EXERCISE: Write a program temperature.c that asks the user for a temperature in Fahrenheit and prints the temperature converted to Celsius. Recall that $x$ degrees Fahrenheit is $5(x-32)/9$ degrees Celsius. For example:

    What is the temperature in degrees Fahrenheit? 42.5
    42.500000 degrees Fahrenheit is 5.833333 degrees Celsius.
    

    (If you're interested: there are variants on the formatting specifiers that limit the number of zeroes. Read up on that further if you like.)

    Add, commit, and push to GitHub.

  4. Control structures

    C has basically the same syntax for if statements for branching and for, while, (and do/while) for looping as Java. However, there are a couple of differences you should be aware of:

    • In older versions of C, you might run into problems if you declare a for loop's index variable within the loop header.
      /* But may cause you trouble. */
      for (int i = 0; i < 10; i++) {
         ...
      }
      
      It's safe to put the declaration earlier:
      int i;
      for (i = 0; i < 10; i++) {
         ...
      }
      
      However, you should be using an up-to-date C compiler, so this should not be a problem. In fact, you should treat this error as an indication that your C compiler is too old. Talk to me if this happens.
    • C doesn't have a true boolean type. C only has integers, and treats 0 as false and everything else as true. (This is similar to Scheme's #f, except that C is perfectly happy doing arithmetic on 0 and 1. So (1 + 1 == 2) + (2 + 2 == 2 * 2) is 2 in C, but (+ (eq? (+ 1 1) 2) (eq? (+ 2 2) (* 2 2))) is an error in Scheme.) For example, the following is legal C code (see subtract.c for a full listing):
      int x, y;
      printf("Enter two integers separated by a space: ");
      scanf("%i %i", &x, &y);
      if (x - y) {
         printf("You entered two different numbers!\n");
      } else {
         printf("You entered the same number!\n");
      }
      

      To be more precise, there is actually a type _Bool built in. Variables of type _Bool are only allowed to have the values 0 or 1. (Assigning a value other than 0 or 1 into a _Bool variable results in the variable having value 1; see boolean.c). If you #include <stdbool.h> you'll get the names bool, true, and false, which are aliases for _Bool, 1, and 0, respectively. Use stdbool.h and the aliases to express Booleans, please.

    EXERCISE: What happens if you enter something other than a number when prompted for the temperature for your answer to Exercise 3? Read the "RETURN VALUE" section of man 3 scanf. Modify temperature.c to obtain the return value from scanf, and check it to see if something went wrong. In the event that scanf fails, your program should exit with an appropriate error message. Add, commit, and push to GitHub when complete.

    (Note that the scanf man page gets fairly technical regarding EOF, errno, and a variety of other things. You are welcome to learn about these details, though all I intend you to do for purposes of this exercise is to read the return value from scanf and use it appropraitely.

5. Submission

You have now pushed a number of commits to GitHub. You will continue to push changes to the same repository for the next assignment. As such, the grader and I need to know which commit is the one that we should use for grading. This is handled well in Git with tags.

First, make sure your final submission (including credits.txt) is ready.

git commit -am "All my changes are committed, I may have already done this"
git push

Second, tag the last commit and push the tag to GitHub (a vanilla git push does not push tags).

git tag hw6
git push --tags

Look on GitHub after the push, and click where it says "X commits" (where X is a number). That will show you the commit history. If you click on your most recent commit, you should be able to see the tag listed there.

You cannot undo tagging, and reusing the same tag for a different commit is generally a really bad idea. As such, you should wait to tag until you are sure you have committed the version you want us to grade.

Here's a long tangent. Note that GitHub sort of displays the tags incorrectly: even though in Git a tag is associated with only a single commit, GitHub will show you that same tag if you click on all commits that precede it. (Note that this is making a distinction between Git, the command-line tool, and GitHub, the webserver.) GitHub is trying to be helpful by showing you all commits that contribute to a release, as that's how tags are often used, but GitHub is ultimately doing this wrong. If you want to see your tags at the command prompt, you can use:

git log --tags --decorate=full

Written mostly by Laura Effinger-Dean, with help from Kochan's Programming in C and Java labs written by Dave Musicant and Andy Exley. Some modifications by Dave Musicant, David Liben-Nowell, and Jed Yang.

Start early, have fun, and discuss questions on Moodle.