Homework 7: C Introduction 2
Table of Contents
This assignment is to be done individually. You can talk to other people in the class to come up with ideas and get help with general concepts, but you should not show others your code, nor see others' code. You can get direct help debugging from Anna and any of the course staff (graders, lab assistants, prefects).
1. Get started
Download the folder with the starter code from the link below. Like for previous homeworks, unzip the folder and move it to where you'd like: most likely the folder for the Docker container. Then get started in VS Code.
If when running tests you run into a permissions error, you can fix this by running the following code in the terminal:
chmod a+x test-e test-m
This tells the computer that all users should be permitted to execute (run)
the files test-e and test-m.
2. Your tasks
2.1. Guess a number
EXERCISE 1: Write a program guess.c that picks a random number between 1 and 100 and has the user
repeatedly guess the number in a while loop.
For random numbers, use the random function.
Here is a sample usage:
#include <stdio.h> #include <stdlib.h> #include <time.h> ... unsigned int seed; scanf("%u", &seed); srandom(seed); //srandom((unsigned int)time(NULL)); int rand_num = random()%100 + 1;
Note that with the above approach, you'll produce the same random number every time you run your program, based on the seed. That's good, as it allows our testing code to work reliably. That said, I've shown commented code above that you can use just for fun that lets you pull the random number seed from the system clock, rather than based on user input.
A run of your program might look like this:
Enter a random seed: 112233
Guess a number: 50
Too high!
Guess a number: 37
Too low!
Guess a number: 43
Too high!
Guess a number: 40
Too high!
Guess a number: 39
Correct! Total guesses = 5
You can take a look at the tests to see how we're checking your work. In particular, it's important that you say "Too high!", "Too low!", and "Total guesses = " followed by the number of total guesses. Additionally, it's important that the seed is the first thing requested from the user, followed by the guesses.
If a user guesses a number that is not between 1 and 100, tell them it's out of range and do not include that guess in the count of total guesses.
2.2. Arrays
You can declare an array in C by giving the type and size of the array. See arrays.c for a sample program.
Just like in Java, C has arrays that allow you to have a sequence of values. You can allocate space on the stack or in the heap. Here's an example of the difference:
int array_stack[10];//Allocates a 10 integer array on the stack int *array_heap = malloc(sizeof(int)*10); //Allocates a 10 integer array on the heap
In either case, you can use [] notation or derefencing to read and write elements:
array_stack[3] = 5; //equivalent to: *(array_stack + 3) = 5;
Unlike Java, there's no reasonable way to find out the length of an array, so you need to keep track of an array's length yourself. And if you write past the end of an array, there's no check or "out of bounds error" - it will just modify whatever happens to be next in memory!
In arrays.c, the following line is commented out:
array[10] = 5;
Uncomment that line, and observe how the value of x changes. Note that clang issues a warning that you're doing a bad thing, which you are, but the program should still run. What does this imply about the layout of the variables in memory? After you've experimented, re-comment out this line to get rid of the compiler warning.
EXERCISE 2: Create a new file sums.c to ask the user how many numbers
they'd like to store and what number they'd like to start at. Allocate
an array to store that number of numbers, and write that number of
numbers into the array, consecutively and starting at the number they
say they'd like to start at. Then, print out the sum of the numbers at
odd indices in the array.
Here's an example of me interacting with my program:
How many numbers do you want to include? 4 What number do you want to start at? 7
2.3. Pointers
The variable array is actually a pointer to the first element of the array.
A pointer is a special type containing the address in memory of some data. You
can use pointer arithmetic to access the elements of the array.
printf("Array address: %p\n", array); for (int i=0; i < 10; i++) { printf("Array element at address %p: %i\n", array+i, *(array + i)); }
Here the expression array + i gives the address of the i-th element in the array.
The expression *(array + i) retrieves the integer value stored at that address.
Here's a quick summary of C's basic pointer operations (we'll discuss more in class).
&E evaluates to the address of an expression E. *p gives the value stored at the
location pointer to by p. p must have a pointer type, indicated by a * in the
type declaration. For example, int * is the type representing a pointer to an int.
So if p has type T *, the *p has type T.
As an example, read and run the program
pointers.c (in the starter code folder).
Try drawing the execution of the program on paper, with boxes for memory locations and
arrows for pointers. Can you see how we end up with the final values for a and b?
Hopefully it also makes more sense why we pass arguments to scanf using the & operator.
EXERCISE 3: If you declare two variables in a row, i.e., a and b, does the second
one have a higher memory address or a lower one? Add an "if" to pointers.c to determine
if the memory address for b is higher or lower than the one for a. If it is
higher, print "higher" to the screen; otherwise, print "lower".
2.4. Structs
C does not have classes or objects. However, you'll often run into situations where you want to group related values together. For this purpose, you can create a struct, a special kind of user-defined type.
Structs are defined using the keyword struct, a name for the struct, and a list of member variables
within curly braces. For example, here's a struct to represent a student
(see student.c for the full code listing):
struct Student { char *first_name; char *last_name; int id; };
You can create an instance of a struct type by declaring it with the
struct keyword, and access member variables using the dot (.)
operator. See student.c for details.
EXERCISE 4: Create a new file complex.c. In this file, add a
struct type struct Complex containing two doubles representing the
real and imaginary parts of a complex number. Add a function,
multiply_in_place that has two parameters, both pointers to
variables of type struct
Complex. After this function is run, the two input pointers should reference the
product of the two complex numbers they originally pointed
to. multiply_in_place should not return anything (i.e., its return
type is void). Note
that if you have two complex numbers \(c_1\) and \(c_2\) whose real parts
are \(a_1\) and \(a_2\) and whose imaginary parts are \(b_1\) and \(b_2\),
respectively, then the real part of their product is \(a_1 * a_2 -
b_1 * b_2\), and their imaginary part is \(a_1 * b_2 + a_2 * b_1\).
In main, the program should prompt the user for the two complex numbers and then print out both numbers before and after calling the multiplication function. Here is a sample run, which you should match to make sure you pass the tests:
Enter real part of c1: 2 Enter imaginary part of c1: 5 Enter real part of c2: 3 Enter imaginary part of c2: 4 Before multiplication: c1: 2.00 + 5.00 i c2: 3.00 + 4.00 i After multiplication: c1: -14.00 + 23.00 i c2: -14.00 + 23.00 i
To get the output looking like I did with two decimal places, I wrote a short function for printing a complex number:
/* Prints a complex number with two decimal places of precision */ void print_complex(struct Complex num) { printf("%10.2f + %10.2f i\n", num.real, num.imaginary); }
Please copy and paste this function into your code and use it to allow you to match my output exactly.
Note for testing scripts: The scripts won't directly check that your
multiply_in_place function is working in the manner stated here,
with the values that are pointed to by the inputs actually being
changed. However, we will consider not doing this to be an instance of
hyper-tailoring your code to our tests. One way to ensure that you are
not doing so is to make sure you call print_complex on a variable
c1 twice and on a variable c2 twice, with the only non-printing thing that
happens in between being a call to multiply_in_place.
Note that while the testing scripts are ignoring your spacing, they are checking for exact matches on the colons/etc that I have above.
3. How to test and submit your work
3.1. Testing your work
As with previous assignments, there are M tests and E tests. See the section of Scheme Intro 2 labeled "How to test your work" if you need a refresher on how to run these tests. For C homework assignments after the first two, the tests will also use Valgrind to check your code. Make sure to run the tests in the Docker environment.
3.2. Submitting your work
3.2.1. Acknowledging help
Create a file called CollaborationsAndSources.txt, and in it,
indicate in what ways (if any) you collaborated with other people or consulted
outside resources on this assignment.
Did you share strategies with anyone else? Talk about any annoying errors and get advice? These are
fine things to do, and you should note them in the CollaborationsAndSources.txt
file. Give the names of people you talked with and some description of
your interactions. If you used any resources outside of notes taken in class or the homework/lab starter files that
Anna provided, that is also something to note.
If you didn't talk with anyone or use any outside sources, please note
that explicitly in CollaborationsAndSources.txt.
3.2.2. Generating the submission file and uploading to Gradescope
After completing CollaborationsAndSources.txt and finishing the
assignment, run ./zipitup (if you get a permissions error, run chmod u+x ./zipitup first).
This script will create a zip file XXX_submission.zip where XXX is the assignment name or number.
Upload this file to Gradescope.
On Gradescope, you should see autograder output for the same tests as you ran locally. There is a small chance that the tests will fail on Gradescope, even if they passed on your own computer. Usually this means that you coded something in a different way than we expected when writing the autograders. We will be grading based on the results of the tests on Gradescope, so make sure to pay attention to the results of the tests there. (If you run into this issue, check that you are submitting to the correct assignment on Gradescope. Then try resubmitting and rerunning the autograder. If the error messages are legible, feel free to debug further, but it's also fine to reach out to Anna or the graders at this point.)
3.2.3. Grading
You'll earn at least an M if the following conditions are all met:
- All M tests pass
- A visual inspection of your code shows that you have not hyper-tailored your code to pass the tests. Hyper-tailoring your code would be doing things like checking for the exact input from the test, meaning you haven't written your code in a way that would work for similar inputs.
- You have a
CollaborationsAndSources.txtfile with the information described above.
You'll earn an E if:
- All conditions above for an M are met.
- All the E tests pass.
- Your code is not significantly more complex than needed to accomplish the task.
- You have a comment before each function describing what the function does (its input/output behavior, not a play-by-play of "first checks… then makes a recursive call …").
- Your code follows good style convention (we will get more strict on this as we progress with each language).
If your code does not meet the criteria for an M or an E, then you'll earn a
grade of NA (Not Assessable) or SP (Some Progress) for the assignment. Not Assessable means that your submission does not
have an attempt at one or more functions in the assignment. Some Progress means
that your submission has some code (in the correct language) for all functions in the
assignment, but that the code does not pass all of the M tests, is
hyper-tailored to the tests, and/or is missing a CollaborationsAndSources.txt
file.
You are welcome to revise your submission until the revision deadline (see Gradescope).
If you revise your submission, double-check to make sure that the autograder tests pass and that you
have included CollaborationsAndSources.txt.
Good luck, ask questions, and have fun!
Assignment modified from one originally designed by Dave Musicant - Thanks for sharing!