Lab 1 - Introduction to C
Lab goals:
- Orient you in how to do some basic things needed for COMP 321, and help you set up your computer so that you can do them.
- Emphasize how the programming principles learned in COMP 140, 182, and 215 apply to C programming. Too often, students neglect to use their previous skills when developing low-level software.
- Introduce some specific C syntactic details.
You Will Need a Rice NetID and a GitHub Account
For this course, you will need to have a Rice NetID-type account to be able to log in to Rice's CLEAR Linux servers. If you do not have one, you can apply for one here.
You will also need a GitHub account for this course. GitHub hosts an instance of the git distributed software version control and change tracking system. Many of you already have a GitHub account. If you do not have one, you should create one now. As a computer scientist, your GitHub account will follow you forever, so choose your username wisely.
To access GitHub using git commands from the
command-line, such as from CLEAR, you will also need to
create what GitHub calls a personal access token
(or
PAT). To do so, refer to the
GitHub documentation for creating a personal access
token. It is easiest to use a classic
token.
Specifically, follow the steps shown in this GitHub
documentation under the heading Creating a personal
access token (classic)
.
Where it asks you to provide a Note
, give a
description such as For COMP 321 command-line
use
. Where it asks you to select the expiration period
for your new token, select whatever period you like, such
as perhaps the end of the semester. And where it asks you
to select the scopes for your new token, make sure the
checkbox next to repo
is selected. Then click
on the Generate token
button.
This will then show you your newly generated GitHub personal access token. It will look like an ugly 40-character long line of random characters. Copy the new personal access token to your clipboard, and then paste it somewhere safe, such as a private file on your own computer or your password manager. Note that you must copy it immediately, as there is no way to see the token again once you leave that page. You will need to use this personal access token as if it was your GitHub password when using git commands on the command-line. Basically, your real GitHub password can still be used to access the GitHub website and acts as the long-term authenticator for your GitHub access, but your personal access token acts like a shorter-term password for your GitHub account when accessing GitHub from the command-line. You only need to generate a personal access token once, or when the one you had been using expires.
Register for COMP 321 on Piazza
This semester, we will be using Piazza, a web-based platform that will allow you to post questions about the course material and to receive answers from the instructors, from the TAs, and from your fellow classmates.
If you have not already done so, please register for COMP 321 on Piazza by going to the Piazza registration page. You will need to register using your @rice.edu email address.
Throughout the semester, check Piazza frequently for any new questions, answers, announcements, or other information. To do so, go to the COMP 321 Piazza page, log in to Piazza if you are not already logged in, and click on "Q & A" at the top of the page, if it is not already selected. In addition to posting and finding answers to your own questions, reading other questions and answers on Piazza can help you find additional valuable information including assignment assistance and clarifications. And if you encounter a new problem or question, you may well find an answer to it by searching on Piazza.
When using Piazza, please observe the following guidelines:
- Before posting any question, always search the existing Piazza posts first to see if your question has already been asked. If it has, and you don't think the answer(s) are clear, please follow up in that post, rather than creating a new one. As the semester progresses, you should also keep up with reading all of the postings on Piazza and all of the answers there, since this may enable you to discover the answer to some new concern you have even before you realize you need help with that!
- Note that although you can post anonymously, the staff will always be able to see who you are. Be polite, considerate, and do not ask frivolous questions or post frivolous content. Always be aware of how your statements might be perceived by others.
- When posting questions or answers on Piazza, please be careful about what you post, to avoid inadvertently violating the course's Honor Code policy. Specifically, do not include portions of your code (even if it doesn't work) in public questions or answers on Piazza. Please use Piazza for questions about the material for the course, not about how to fix your particular solution.
- If you are asking a question about the material in the class, your post should always be public. You should use private posts only when you need to talk privately with the staff. If your post is of a personal nature, you likely should bring it up with the instructors directly, rather than through Piazza. If your post absolutely requires you to include information about your solution, then your post should certainly be made private. To make your question private, select "Instructor(s)" (rather than "Entire Class") for "Post to" at the top of the posting page, so that only the course instructors and TAs can see your posting.
- Piazza is not always the best avenue to receive help in the course. It may be better to discuss some issues during office hours, for example.
Basic Computer Pragmatics
All of your programming assignments in this class must be compiled and tested on Rice's CLEAR servers. In particular, you can log in to them over the network using ssh from your own personal laptop or desktop computer. Moreover, it doesn't matter whether your computer is on-campus or off-campus for accessing CLEAR using ssh. Also, note that you don't need to use (and shouldn't use) Rice's VPN when using ssh. You can log directly in to any of the CLEAR servers using just ssh using the host name ssh.clear.rice.edu. This name automatically chooses one of the available servers.
Specifically, to log in to one of the CLEAR servers, you will need an ssh client program on your own computer. The details for which ssh client you use will differ, depending on whether your computer is a Mac or a Windows machine.
CLEAR Host SSH Key
If you see a message about the host SSH key on clear, the following is the correct fingerprint as of 1/10/24:
SHA256:nQMQP3qbXdN3NemQvbgka7/d/zv2Ydj6e4jcS1NdQTA ssh.clear.rice.edu (ED25519)
Using a Mac to log in to CLEAR
To log in to CLEAR from your Mac, follow these steps:
- Open the
Terminal
application. - At the terminal's command line, type (this is the
same as for logging in from a Windows computer, below):
ssh yournetid@ssh.clear.rice.edu
to log in to CLEAR. Log in using your Rice NetID and password. When logging in from your own personal computer, your username on that computer may be different than your Rice NetID. For yournetid above, use your Rice NetID.
If you get a notice saying that the authenticity of the host can't be established and asking if you are sure you want to continue connecting, answer on
yes
. You only need to do this the first time you connect to a new server.Note: When entering commands, you must precisely duplicate the given capitalization.
Congratulations! You are now logged in to the CLEAR system and ready to go! You are at the command prompt of the command-line interface (also called a shell) and can now enter commands into CLEAR.
When you are done using CLEAR, you should log out of the CLEAR server by typing the following command at the CLEAR command prompt:
logout
Using a Windows computer to log in to CLEAR
To log in to CLEAR from your PC, follow these steps:
- Open the
Windows PowerShell
application. - At the terminal's command line, type (this is the
same as for logging in from a Mac computer, above):
ssh yournetid@ssh.clear.rice.edu
to log in to CLEAR. Log in using your Rice NetID and password. When logging in from your own personal computer, your username on that computer may be different than your Rice NetID. For yournetid above, use your Rice NetID.
If you get a notice saying that the authenticity of the host can't be established and asking if you are sure you want to continue connecting, answer on
yes
. You only need to do this the first time you connect to a new server.Note: When entering commands, you must precisely duplicate the given capitalization.
Congratulations! You are now logged in to the CLEAR system and ready to go! You are at the command prompt of the command-line interface (also called a shell) and can now enter commands into CLEAR.
When you are done using CLEAR, you should log out of the CLEAR server by typing the following command at the CLEAR command prompt:
logout
If you run into any issues, you may also find it helpful to review Rice's documentation on logging into CLEAR from Windows.
C Survival Skills, Part 1
Like with Java programs, you will normally follow a three-step process:
- Write a C program.
- Compile the program.
- Run the program.
In this class, the following steps should all be done on the CLEAR system, once you have logged in to CLEAR as described above.
-
Writing will be done in an editor. You may use whatever editor your prefer. If you are unsure of what to use, we recommend either Visual Studio Code on your local machine connected remotely to CLEAR or nano directly on CLEAR.
Visual Studio Code:We recommend that you install and use Visual Studio Code (VS Code). VS Code has become the gold standard of code editors. It uses familiar keyboard shortcuts and menus so you can start to use it very quickly. However, it is also extremely powerful and has a myriad of features to aid in programming that you will find helpful as you learn more about the editor. Furthermore, it has a rich extension ecosystem in which you can find an extension to do almost anything.
In particular, VS Code can be used for remote develoment. This means you can install and use VS Code on your local machine while you work on CLEAR. To do so, you should install the Remote Development extension. Once you have done so, you can access CLEAR as follows:
- Press F1, Ctrl+Shift+P, or Command+Shift+P to open the Command Pallete and run the Remote-SSH: Connect to Host... command.
- Enter your user and host: netid@ssh.clear.rice.edu
- You will be prompted for a password (the
placeholder text will say
Enter password
), enter your netid password. - You will be prompted for a password again,
however this time it is actually the prompt
for 2-factor authentication (the placeholder
text will
say
(netid@ssh.clear.rice.edu) Duo two factor login for...
): 1 - Duo Push, 2 - Phone Call, 3 - SMS passcodes. Just enter a single number (1, 2, or 3). - Complete the 2-factor authentication.
Note that you need to pay attention to the placeholder text in the password input box in order to know when you should be typing your password (perhaps because you mistyped it) and when you should be typing a number for the Duo prompt.
If/when it asks you for the Host Operating System, you should select
Linux
, as the CLEAR servers are Linux systems.Some students have found that they need to click
Details
in the message box that appears when logging into CLEAR via VSCode in order to access the Duo prompt. If things seem like they are not making progress, you should clickDetails
and see if it provides any useful information.You can easily find plenty of information on the web about using VS Code effectively for programming.
Nano: If you would prefer to get started quickly and set up VS Code later (or you would prefer not to use VS Code at all), we recommend nano, a small, easy to use editor that provides on-screen information about the most common editing commands available at any time. You can, however, use any editor you would like. Other common editors in the Unix environment include emacs and vim.
Many things about the behavior of nano are configurable through a file called .nanorc located in your Unix "home directory" (the directory or "folder" you are in just after logging in). Every time you run nano, the program will use your .nanorc file to determine various things about the program's behavior. You only need to set up your .nanorc file once, not each time you run nano.
We have provided a .nanorc that you can use, but you are free to create your own, to modify your own copy of our supplied .nanorc, or to use nano with no .nanorc. If you want to use our provided .nanorc (highly recommended), then you can install it for your use by the following two shell commands on CLEAR:
cd cp /clear/www/htdocs/comp321/.nanorc .nanorc
The cd command here changes your "current directory" to be your "home directory", and the cp command makes a copy of our provided .nanorc in your home directory. Be careful to type these two commands exactly as shown above.
To run nano, you may use the following command:
nano filename
where filename is the name of an existing file you want to edit or a new file you want to create.
In nano, you can move around within the file naturally, such as using cursor keys (i.e., the "arrow" keys), the "Page Up" and "Page Down" keys on the keyboard, etc., and you can delete characters naturally using the "Delete" or "Backspace" keys on your keyboard. For most characters that you type, they are simply inserted into the file as you type them, at the current cursor position.
The bottom 3 lines on the screen have a special purpose. The first of these 3 lines shows useful status information. For example, when you first open a file in nano, this line tells you how many lines exist in the file or tells you that this is a new file. The 2 lines below this show the most common editing commands that you can use then.
Editing commands in nano are generally typed as a single "control character". For example, typing a control-X causes nano to exit, typing a control-W allows you to search for words or other text within the file ("Where Is"), and typing control-G allows you to get online help within nano. As is common in many programs, a control character is displayed as, for example, ^X, meaning a single control-X character. To type such a control character, press and hold down the "Control" (or "Ctrl") key on your keyboard, then press and release the particular character (such as X), and finally release the Control key on your keyboard.
The ^K ("Cut Text") and ^U ("UnCut Text") commands allow you to effectively move a whole line (or a part of a line) of text within your file. If you type ^K at the beginning of a line, nano "cuts" the entire line; if you type ^K somewhere in the middle of a line, nano "cuts" just from the current cursor position on that line to the end of the line (this behavior is determined by a setting in the .nanorc file). To bring the cut text back at some other place in the file, just move the cursor position to where you want to place the cut text and use the ^U ("UnCut Text") command.
When you are done editing your file in nano, you must save that file before exiting nano. To do so, use the nano ^O command (meaning "WriteOut"). You will be prompted for "File Name to Write", with the default being the existing name of the file you are editing. If you just want to save it to the same name, just hit the "Enter" key on your keyboard. If you have made changes in the file in nano and try to exit nano (using ^X) without having saved your file, nano will ask you if you want to "Save modified buffer" first. Answer Y to save your changes to the file and then exit nano, or answer N to exit without saving your changes to the file (the file will still exist, but any changes you made to it while running nano this time will be discarded). If you want to continue in nano and not exit, answer with a single ^C character.
If you are looking for more information on how to use nano, there are many tutorials available online, including this one and this one. And in addition to the help available within nano itself (available using control-G), the official documentation for the version of nano that is installed on CLEAR is available on the web.
-
Compiling may be done either may be done by typing the compilation command at the Unix command prompt or at the control of a compilation manager like make.
On CLEAR, an appropriate command would be
cc -Wall -Wextra -Werror -O2 program.c -o program
This command and its options have the following meanings:
- Use the cc C compiler.
- Use the latest version of the C language that is supported by this compiler.
- Enable all standard and some extra warnings.
- Treat any warning like an error and reject the program (giving you a chance to fix that problem and try compiling again).
- Ask the compiler to work harder to generate machine code that runs faster.
- Use the single source code file program.c.
- Output the resulting executable (the result of compiling this source code file) in a file named program.
We recommend that you always use -Wall and -Wextra because they instruct the compiler to check for common programming errors, like using uninitialized variables. Technically, programs that provoke warnings are not illegal C programs, but they are unlikely to produce correct results. You'll see and use more compiler options later. Don't be surprised if the compiler finds lots of errors in your code and doesn't produce an executable.
Nonetheless, most compilation in Unix is usually done under the control of make. A later lab will fully explain make, but even the first projects will set things up for you to use make without you needing to know the details of its operation.
-
Running your program is then simply a matter of typing its full (i.e., absolute) path name (e.g. /a/b/c/program), or a relative path name (e.g., ./program) at a Unix prompt.
Relative names are often much shorter and easier to type. In Unix, a period (".", usually pronounced "dot") is shorthand for the current directory, so prefixing "./" on a file name means to look for it in the current directory:
./program
Similarly, 2 periods ("..", or "dot dot") means to look for it in the parent directory, 1 level up:
../program
Note: If a single name is given to the shell (e.g., program) with no slashes in the name, the shell uses a search path to look for the program to run. A search path is simply a list of directories to look through to find executable programs of the given name.
If the current directory (i.e., ".") is not on your Unix command search path, then you will need to prefix "./" to your program's name to run the program. For example:
./program
Otherwise, an error message such as the following will be printed:
program: Command not found.
To check your Unix command search path, you can enter the following command:
echo $PATH
For programs in the current directory, running them using the "./" prefix as above (e.g., "./program") is always the best approach, in any case. And it is recommended not to include "." on your Unix search path so that you don't accidentally end up running something from the current directory when you, for example, mistype the name of some standard Unix command.
Also, make sure you don't name your program the same as
any of the standard Unix programs or commands. Otherwise,
you're likely (depending on your $PATH
) to run
the standard program or command and not your own program.
To check whether or not a name is that of some existing
command, e.g., the name test, try the
command
which testYou'll find that test is the name of an existing Unix command (located in the file /usr/bin/test).
A Brief Introduction to Generic git Concepts
In this class, we will be using the git
program for providing starter
or template files to
you, and you will be using git for managing and
saving your source code files and for submitting your work
for grading.
The git program is a complex system with hundreds of operations and variations. But for now (and for this lab), however, we will stick to the 6 most fundamental operations of git. For the most part, these operations should suffice for most of the class. We will supply you with more information on git if and when it becomes necessary.
The fundamental object that git maintains is called a repository. For the moment, think of a repository (or "repo" for short) as a kind of augmented directory (or folder). The repository contains source code files, augmented with a history of changes to those files. This means that you can go back to any previous version of some file that has been committed to the repository.
The git system distinguishes between
local and remote copies of a repository.
The normal git workflow begins by cloning
a remote repository onto the local
system, then making several commits
to the local
copy, and finally pushing
the changes back to the
remote repo.
One other wrinkle in the git workflow
involves local commits. Rather than a single operation to
accomplish a local commit, git uses two
operations to accomplish this task. The first stage is used
to add
any new or modified files to a list of files
to be committed, followed by the actual commit
operation to commit those files to the local repo. In
particular, you add the files to be committed in
step 1 (all on a single add
command or on
multiple individual add
commands), followed by a
single commit operation to commit all of the added files at
the same time.
Creating a git Repo for Something in COMP 321
For this class, our remote git repos will use
GitHub. As mentioned above, GitHub hosts an
instance of the git system, and your remote
git repos will be stored in the GitHub
cloud.
Your local git repos will be
stored on Rice's CLEAR system.
The first step for any lab or assignment in this class is to use your browser to go to the provided link for the starter code. For this lab, that link is:
https://classroom.github.com/a/iIZDEeMS
If you are not already logged in to GitHub, it will ask you to do so now.
Next, since this will be your first use of GitHub for
this class, GitHub will first show you a web page titled
Join the classroom
, where you need to associate your
GitHub account (which you are logged in to GitHub as) with
your netid (your "school's identifier" for you). This is
necessary so that we can then correctly attribute your
GitHub submissions to you for grading them, for
this lab and all following labs and assignments. Scroll
down to find your netid and click on it. GitHub will ask if
you are sure you want select the netid you selected, and if
so, click on OK
. You only need to do this step
once; for the rest of the labs and for the assignments,
GitHub should not need to ask you this.
Following this step, you should next be presented with a
web page asking you to "Accept the assignment". Please
click on the green Accept this assignment
button.
GitHub will then do a bit of work to create a new remote
repo for you for this assignment, based on the starter code
we have provided. This may take only a few seconds, or it
may take a few minutes. Occasionally use your browser to
refresh the view of this web page, until you get a new page
saying You're ready to go!
.
On this You're ready to go!
page, you should also
see a link to your new remote repo. This link will begin
with https://
and will end with your own
GitHub username. Use the mouse on your computer to
copy that entire link (beginning with the
https://
text and ending with your GitHub
username) to the clipboard on your computer. You
will use this link in a moment to clone this
remote repo onto CLEAR as your local repo for this
lab.
Log in to the CLEAR system if you have not already done so. In your home directory on CLEAR, type the following command:
git clone paste your repo link from the clipboard here
In other words, type the words
git clone
followed by a space, then paste the link from your
clipboard (the link you copied above) onto that same
command line. Then hit the Enter
key on your
keyboard to complete the command.
You will be prompted for your GitHub username
and password. As described above, you need to use your
GitHub personal access token as your GitHub password
here (e.g., use your mouse to copy and paste your
personal access token here, from whatever file you saved it
in when you generated it above).
Once the clone operation is complete, you will have a directory named
lab-1-introduction-to-c-namewhere name is your GitHub username. Confirm that you have the directory by typing ls on the command line. (ls is the Unix command to list files).
Now, change your current directory into this new directory by typing
cd lab-1-introduction-to-c-namewhere name, again, is your GitHub username. The cd command name is short for
change directory. Now that you are inside this new directory, type ls again. You will see two files of starter code and a
READMEfile:
fact_loop.c fact_rec.c README.md
The README.md
file contains the instructions for the
exercises in this lab, as will be the case throughout the
semester. The README file is in markdown format; you can look
at it in any text editor and it will mostly make sense.
However, it is intended to be viewed from within the GitHub
web interface, where it will be formatted nicely. You should
see it below the list of files in the repository, as GitHub
automatically displays the README.md
file for a
repository.
At this point you will edit, compile, and run your files
several times. When you have completed a unit of
work
, you will want to commit your changes to your
local repository. In addition, after a few
commit
steps, you will want to push your changes back to the
remote repository for safekeeping. (We are being vague
here, as the amount of work you do before a commit or push
is a judgment call.)
A Complete Example git Workflow
In this section, we will give some details on the git operations needed to enact a simple (and typical) COMP 321 workflow.
Step 0. Clone the starting repo (You have already done this step for this lab, presumably)
git clone your personal assignment repository name
Step 1. If you have not done so already, cd to your local directory (repo).
cd your local assignment directory nameWork in your local directory; perform some edit-compile-test cycles.
Step 2. When at a good
point, do a local
commit.
Step 2.1: See what has already been added using
git status
Step 2.2: Add all files we want to commit, both files that have changed since last commit as well as newly created files.
git add names of changed files and new filesNote, you should always include in your repo all files that you created in the lab, other than those (such as the output of the compiler) generated automatically from other files. Think of this as including just the files necessary to backup and to be able to recreate your work. Do not simply add all files to your repo; for example, do not simply say something like
git add .or
git add *, and never add any
corefiles to your repo. Only add the actual files that should be in your repo.
Step 2.3 (Optional): Confirm the status
git status
Step 2.4: Commit the changes to the local repo
git commit -m 'a brief 1-line message describing the commit changes'
You may repeat steps 1 and 2, before deciding to push the local commits to the remote repository. (You may also, instead, decide to push after every local commit, if you want to.)
Step 3. Push local repository changes back to remote repository
git push origin master
PLEASE NOTE: You must commit and push your final result! This is how we are able to grade your submissions!
You can push as many times as you want. We always take your latest version (up to the deadline) for grading.One other operation you may want to use will list the commit messages from the history.
git log
It is also highly recommended that you set up the credential cache using the following git command. This will save you from having to reenter the credential upon every interaction with GitHub.
git config --global credential.helper 'cache --timeout=7200'You are also free to change the timeout (the number of seconds to cache credentials) as you like, although we recommend setting the timeout to 7200 seconds.
Finally, in common Unix fashion, the git program supplies general help
git helpYou can also get help with a specific git command. For example
git help commitshows help for the commit command. Other git commands also have help.
Some C Fundamentals
Don't forget what you've learned before, especially about designing and testing programs. For most programs and functions, you should follow all the same design steps as you learned in previous COMP classes.
Now we need to see some basic C syntax. To begin, you'll write small programs that are just transliterated from ones written in more familiar languages, like Python. In fact, the syntax should be familiar to those of you who just took COMP 215, because Java's syntax is based on that of C. For brevity, we'll omit the steps you should be using in designing the programs, and show only the final code.
A Simple Recursive Function
For now, we will use numbers as our only data. Examples with more interesting kinds of data will be presented in the upcoming lectures and laboratories.
Let's write sum_upto
, which computes the
sum of the integers 0, 1, ..., n. In
Python, we could write
def sum_upto(n):
"""
Returns 0 + ... + n.
"""
if n == 0:
return (0)
else:
return (n + sum_upto(n - 1))
A C transliteration would be:
/* Returns 0 + ... + n. */
int
sum_upto(int n)
{
if (n == 0)
return (0);
else
return (n + sum_upto(n - 1));
}
Some remarks:
- In C, the characters /* begin a comment, and */ end a comment. Here, a comment is used to represent the same information as in the docstring in Python.
- The type information that is recommended to be in the docstring in a Python function is required in C. It is a common convention to put the function type on a separate line.
- An int is the same thing as a mathematical integer, except that it has a maximum and minimum value. The difference between an int and a Python integer is that an int has these bounds. You'll see more about C types in class.
- C requires various extra syntactic items like braces and semicolons. A brace groups a series of statements and is required in a function definition. Semicolons end statements. The if...else... is a single compound statement containing other statements.
- We must explicitly state what value is to be returned from the function with return.
- The equality test is a double equals sign. Using a single equals sign is an assignment, and confusing these two different operators is a common mistake that can lead to obscure errors.
A complete C Program
Like Java objects, C functions don't stand on their own as complete programs. We need a main() function to start the program. To make it useful, we also need at least a little bit of output.
A sample main() for the above sum_upto():
#include <stdio.h>
/* Returns 0+...+n. */
int
sum_upto(int n)
{
if (n == 0)
return (0);
else
return (n + sum_upto(n - 1));
}
int
main(void)
{
int n1 = 3;
int n2 = 5;
printf("The sum of 0..%d = %d.\nThe sum of 0..%d = %d.\n",
n1, sum_upto(n1), n2, sum_upto(n2));
return (0);
}
Some remarks:
- We've hardcoded a couple inputs to sum_upto(). A later lab will describe how to get input from the user at run-time.
- If you want some result printed on the screen, you have to do it explicitly. Using printf(), as here, is the most common way in a C program to print something. The first argument to printf() is a description of the format of the output. The #include line brings in the declaration of printf(). Thus, it is somewhat similar to the import statement in Java or Python.
- The definition of sum_upto() should occur first, so that main() sees the definition. Later, we'll see how to avoid this ordering dependence.
- main() returns an int. Usually, by convention, this should be 0, meaning that the program completed successfully. Any other value indicates an error, but there is no universal convention mapping integers to specific kinds of errors. Unix scripts often use these return values to check for errors.
- This particular implementation of main() expects no arguments. Hence, the void where the argument list would otherwise appear.
- As in Java, all variables must be declared before they are used. As in Java, a declaration must also specify a type. However, unlike Java, the C language standard that existed before 1999 required that all variables must be declared immediately following the open brace and before any other kind of statement. Although the current standard has eliminated this restriction, many C programmers still abide by the old rules.
- Also, as a matter of style, most C programmers separate the variable declarations from the subsequent statements with a blank line.
- The format string argument to
printf()
has escape sequences, such as%d
, indicating how the remaining arguments should be printed. The\n
sequence in the format string means to print anewline
character, ending that line of output. We'll explore I/O more in a later laboratory.
Alternative: Loop-based Programming
In low-level programming, loops are far more common than recursion.
In the examples below, the corresponding pieces of code are colored in like colors.
- Initialize the loop counter.
- Initialize the accumulator.
-
Repeatedly,
- Test whether we should continue.
- Update the accumulator.
- Update the loop counter.
- Return the accumulator.
One way to rewrite the
sum_upto
function in Python using loops
is
def sum_upto(n): """ Returns 0 + ... + n. """ total = 0 for i in range(n + 1): total += i return total
Two C loop-based versions are
/* Returns 0+...+n. */ int sum_upto(int n) { int total = 0; int i; for (i = n; i > 0; i -= 1) total += i; return (total); } |
/* Returns 0+...+n. */ int sum_upto(int n) { int total = 0; int i = n; while (i > 0) { total += i; i -= 1; } return (total); } |
Some remarks:
- Unlike Python and Java, in C, the loop variable is always explicit. (Note: We could have used n as the loop variable, but that's arguably poor style.)
- In C for- and while-loops, we loop as long as the test is true, and stop when it is false.
- i -= 1 is shorthand for i = i - 1. It is preferable because the longer version gives us an additional chance to mistype the variable name. It also corresponds to the natural English "Subtract one from i.". (You can also use i-- or --i when you want to subtract one, but the difference between the two can be confusing to beginners.) Efficiency is not a consideration among these alternatives, because they typically compile to the same machine code.
- Unlike in Java or C++ for loops, the C language standard that existed before 1999 didn't allow the declaration of the loop variable (here, i) in the for statement. Although the current standard has eliminated this restriction, many C programmers still abide by the old rules.
- As a general rule of thumb, use for loops when you have a predetermined bound on the number of iterations (here, n), and while loops when you don't (e.g., looping until you receive a specific input).
Using Function Prototypes
C requires function prototypes in order to know the arguments and return types of a function that has not yet been defined.
So, the following code will compile and run correctly:
#include <stdio.h>
/* Returns 0+...+n. */
int
sum_upto(int n)
{
int total = 0;
int i;
for (i = n; i > 0; i -= 1)
total += i;
return (total);
}
int
main(void)
{
int n = 5;
printf("Sum of 0 ... %d is %d.\n",
n, sum_upto(n));
return (0);
}
But, this version with the functions reordered will not:
#include <stdio.h>
int
main(void)
{
int n = 5;
printf("Sum of 0 ... %d is %d.\n",
n, sum_upto(n));
return (0); /* no error */
}
/* Returns 0+...+n. */
int
sum_upto(int n)
{
int total = 0;
int i;
for (i = n; i > 0; i -= 1)
total += i;
return (total);
}
The problem is that, when main calls sum_upto, the C compiler doesn't know what the arguments or return values are for sum_upto. The use of a function prototype will allow the compiler to know this and that the function sum_upto itself is going to be defined later:
#include <stdio.h>
int sum_upto(int n);
int
main(void)
{
int n = 5;
printf("Sum of 0 ... %d is %d.\n",
n, sum_upto(n));
return (0);
}
/* Returns 0+...+n. */
int
sum_upto(int n)
{
int total = 0;
int i;
for (i = n; i > 0; i -= 1)
total += i;
return (total);
}
Some remarks:
- In C, it is good practice to put a function prototype that may be required by many source files into a single header file. All C files that include that header file will then be able to use the functions prototyped therein. This is part of what is contained in stdio.h, for example.
- Function prototypes are necessary in many situations, such as for mutual recursion.
- As long as the prototype matches the definition of the function, it causes no problem even if it is unnecessary for a given program. In fact, some C style guides suggest the definition of prototypes for all functions.
C Survival Skills, Part 2
We will not be
introducing you to all features of C. You should
use a C reference book. Also, the online Unix manual pages
(i.e., using the Unix man
shell command)
describe all of the standard C library functions and other
commands and library
procedures.
Evaluation
The labs are not graded. However, you will receive extra credit for completing them. If, at the end of the semester, your final grade is within 0.25 points from the next letter grade cutoff, you will be bumped up to the higher letter grade based on the labs.
In order to receive the extra credit bump, you must have done the following for each lab:
- Submitted (committed and pushed) your solutions before it was due.
- Made a credible effort at all of the exercises. You are not being evaluated on being fully correct, but rather on whether or not you made a legitimate attempt to solve the exercises.
You may fail to submit at most one lab on time and still be eligible to receive the extra credit bump. You may not use slip days for labs.
Submission
For this lab and all labs in this course, if you finish everything before the end of your scheduled lab session, you may leave early. If you do not finish, you may continue to work on the lab after you leave. In either case, though, be sure to git push your work on the lab before 11:55 PM tonight, to get credit for the lab.
Again, you should always include
in your repo all files that you created
in the lab, other than those (such as the output of the
compiler) generated automatically from other files. Think
of this as including just the files necessary to backup and
to be able to recreate your work. Do not
simply add all files to your repo; for example, do
not simply say something like git add .
or
git add
*
, and never add any
core
files to your repo. Only
add the actual files that should be in
your
repo.