Assignment 7 - C Intro 2
Due: Friday, April 17, 2026, at 10pm
Starter code: a7_c_2.zip
Upload solutions via Gradescope
Goals
This assignment is designed to help you with the following:
- further comfort working with C
- working with arrays, structs, and pointers in C
Collaboration policy
For this assignment, you may work alone or with a partner, but you must type up all of the code yourself. (It is therefore unexpected for two code submissions to be completely identical.)
You may also discuss the assignment at a high level with other students.
You should list any student with whom you discussed the assignment, and the manner of discussion (high level, partner, etc.) in your readme.txt file.
If you work alone, you should say so instead.
Assessment
Core requirements:
- all
CRtests pass - a visual inspection of your code shows that you have not hyper-tailored it to pass the tests
readme.txtcontains your collaboration statement, sources, and reflection
Note that hyper-tailoring in this case 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 other similar inputs.
Advanced requirements:
- satisfy the Core requirements
- your code is not significantly more complex than needed to accomplish the task
- each function has a comment before it describing its high-level behavior
- your code uses good C coding style
Getting started
As before, you should download the starter files, unzip that file, and copy the folder to where you’ll work (probably your ProgrammingLanguages folder). Refer to the Assignment 1 instructions if you’re unsure what to do.
Part A: Random numbers
The C standard libraries provide functionality to generate pseudo-random numbers. Here is an example of using the random function:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
// Get a seed to prep the random-number generator
unsigned int seed;
scanf("%u", &seed);
srandom(seed);
// srandom((unsigned int) time(NULL)); // make it more random!
// Generate a random number
int rand_num = random() % 100 + 1;
printf("You got: %d\n", rand_num);
return 0;
}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, you can use the commented-out line for your own fun to pull the random number seed from the system clock, based on user input.
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.
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 = 5You 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.
- 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.
Part B: Arrays
Just like in Java/Kotlin, C has arrays that allow you to have a sequence of values. You can declare an array in C by giving the type and size of the array. The space for the array can be allocated on the stack or the heap. Here’s an example of the difference:
int array_stack[10]; // stack
int *array_heap = (int *) malloc(10 * sizeof(int)); // heap
In either case, you can use [] notation or dereferencing (*my_array) to read and write elements:
// Store a 5 at position 3 (the fourth element)
array_stack[3] = 5; // equivalent to: *(array_stack + 3) = 5;
Unlike many other languages, there is 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!
See arrays.c for a sample program. Take a look at this commented-out line:
array[10] = 5;Uncomment this 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-commented 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 many numbers, and write that many numbers into the array, consecutively and starting at their preferred start value. 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? 7Part C: Pointers
The variable array in arrays.c 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 (i = 0; i < 10; i++)
{
printf("Array element at address %p: %d\n", array + i, *(array + i));
}Here the expression array + i gives the address of the ith element in the array (this is why we index from 0). The expression *(array + i) dereferences the pointer to retrieve the integer value stored at that address.
Here’s a quick recap of C’s basic pointer operations, as we have discussed in class:
&Egives the address of expressionE*pgives the value stored athte location pointed to byp- a pointer
phas a*in its type declaration, likeint *p;, which is a pointer to anint; ifphas typeT *then*phas typeT
As an example, read and run the program pointers.c from the starter code. 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”.
Part D: Structs
As we have discussed, C does not have classes or objects. However, you’ll often run into situations in which you want ot group related values together (like a “data class” in Python or Kotlin). 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 by declaring it with the struct keyword, and access member variables using the dot (.) operator:
struct Student lulu;
lulu.first_name = "Lulu";It can get kind of verbose to use struct Student everywhere (or whatever the struct type is named), so you can use typedef to effectively give this type a nickname:
typedef struct Student {
char *first_name;
char *last_name;
int id;
} student_t;
...
student_t lulu;
lulu.first_name = "Lulu";Exercise 4
Create a new file complex.c. In this file, add a struct type struct Complex (which you could rename complex_t) 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 (or complex_t). 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 c1 and c2 whose real parts are a1 and a2 and imaginary parts are b1 and b2, respectively, then:
- the real part of their product is a1*a2 - b1*b2
- the imaginary part of their product is a1*b2 + a2*b1
In main, the program should prompt the user for the two compex 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 iTo get the output looking like it does above, you can use this short function for printing a complex number:
/*
* Print a complex number with two decimal places of precision.
*/
void print_complex(complex_t num) // or struct Complex as the type
{
printf("%10.2f + %10.2f i\n", num.real, num.imaginary);
}You’ll want to copy this function into your code and use it to match the expected 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 doing it correctly is to make sure you call print_complex one 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 also that while the testing scripts should ignore your spacing, they are checking for exact matches on the colons and other symbols in the example above.
Testing your work
For this assignment, there are only CR tests. The AD test file just runs 0 tests and always succeeds. Look at the instructions from A2 if you want a refresher on how to run this tests.
For C assignments after this one, the tests will also use Valgrind to check your code, so make sure to run the tests in the Docker environment.
Submitting your work
For this assignment, you will submit four separate files. You are strongly encouraged to run ./zipitup to use that script to gather your files for Gradescope.
C code
The files guess.c, sums.c, pointers.c, and complex.c should contain your functions. Make sure to add a comment above each of your functions (including any helper functions you choose to write) to describe its behavior at a high level (e.g., what it takes as input and what it returns as output).
Readme
For each assignment for this course you will also need to submit a file readme.txt in which you should write:
- Your collaborations with anyone on the assignment
- Your use of any outside sources on the assignment
- Your reflection
You should be specific about your collaborations and sources – they shouldn’t just be empty or lists of names of people. If you worked alone and/or did not use any outside sources, you should say so.
For your reflection, spend a couple of sentences answering the following:
- Were there any particular issues or challenges you dealt with in completing this assignment?
- How long did you spend on this assignment?
Submitting to Gradescope
As always, use ./zipitup to gather your files. Submit the resulting .zip file to Gradescope at this link.
Note that it is possible (although unlikely) that the tests will pass on your computer but fail on Gradescope. If this happens, it’s probably because you coded something in an unusual way. Double-check that you’re submitted to the correct assignment and then check if any error messages can help you troubleshoot. If you’re still having issues, check with Tanya.