Setup
If you haven’t already, do the setup (Windows | OS X). Otherwise, just register for the lab using grinch
: https://grinch.caltech.edu/register and
clone the repository as explained in the setup instructions.
Unlike in CS 2, labs In CS 3 are solo. We will be directly using the office hours queue on grinch (https://grinch.caltech.edu/queue) during lab hours instead of workspaces. So, please enqueue whenever you have a question.
In this lab, we will be writing some functions over C Strings. C Strings can be pretty tricky because of a few “gotchas” which we’ll explore.
Our First Function
The first function you should write is string_print
:
void string_print(char *s)
Prints the contents of s
, one character per line prefixed with the character’s index (out to two digits) and a colon.
For example, the string “Adam” would appear as:
00: A
01: d
02: a
03: m
To do this, you’ll need to learn about the printf
function; you might also find the strlen
function useful.
Both function names link to our documentation on these functions!
Task 0.
Write the string_print
function in task0.c
. Note that strings can literally be treated as arrays; so, you can use the same brackets
syntax that you would in Java.
We recommend writing this function in three stages:
- Print the characters one per line
- Add in the indices
- Fix the indices to be 0-padded to two digits
To test that your function is correct, you should compile and run your code. Then, check that the output matches the example above.
To do this on OS X, run the commands:
clang -Wno-nullability-completeness task0.c -o task0
./task0
To do this on Windows, run the commands:
cl.exe -W3 task0.c
./task0.exe
Memory Allocation and C Strings
The next function to write is string_reverse
. This one involves creating your own string which means we need to learn how to allocate memory in C.
Allocating Memory in C
In Java, we simply write Object o = new Object()
to allocate memory for an Object
. In C, we need to call the malloc
function and
provide the exact size of the allocation we require. This usually looks like Object *o = malloc(sizeof(Object));
. There’s a couple of notable pieces of
syntax in this line:
- There’s a
*
on the left! This indicates that we’re defining a pointer to an Object. In Java, object types are references by default; in C, we need to tell the language we want a reference explicitly with the*
. - The
malloc
function!malloc
is the way we ask our system for memory. It’s roughly equivalent to the first part of thenew
keyword that provides the memory. Be careful though! Unlike Java’snew
,malloc
DOES NOT INITIALIZE THE MEMORY. This means that, when we get it, it’s total garbage. If we want all zeroes, we have to do that manually. - We’re using
sizeof(Object)
.sizeof
is a “function” that gets replaced at compile-time with the number of bytes necessary to hold the type specified in the parentheses. Generally, when we’re allocating a pointer with \(n\) stars, the argument tosizeof
should have \(n-1\) stars.
Strings in C
In C, strings are extremely primitive. They’re literally an array of bytes. As discussed earlier, pointers and arrays are roughly equivalent. So, the type for a string in C is char *
.
Notably, pointers have no concept of “length”. So, strings are “null-terminated” meaning after the last character, there is a “null character” (written \0
) to indicate the end of the
string. A very common mistake in C is not setting the null-terminator or not allocating space for it.
Writing string_reverse
Here’s the specification for the function we want to write. For this one, we’ll walk you through it step by step.
char *string_reverse(char *s)
Returns a new string with the characters in s
in the opposite order. Does not edit the original string.
For example, the input string “Adam” would return “madA”.
Task 1a.
Given the argument char *s
, we need to allocate a new string of the right length to return. Since we’re returning a reverse of the string, it should have the same
number of characters. Using the example above as well as the strlen
function, allocate enough memory for the new string. Note that strlen
DOES NOT
return a value including the null terminator; so, you’ll have to add 1 to the result before passing it to malloc
.
Task 1b. Now, write a for loop to reverse the string and return the reversed string. Note that strings can literally be treated as arrays; so, you can use the same brackets syntax that you would in Java.
There will almost certainly be a bug in your code, but to see it we’ll need to compile and run.
To do this on OS X, run the commands:
clang -Wno-nullability-completeness task1.c -o task1
./task1
To do this on Windows, run the commands:
cl.exe -W3 task1.c
./task1.exe
If all has gone as planned, your output should look something like:
aaaaaaaaaa
bbbbbbbbba
This is not correct. What has gone wrong is we forgot that malloc
does not cleanly initialize the contents of memory for us. We need to explicitly set the null-terminator
in our string.
Task 1c.
Fix the bug by null-terminating the string by explicitly setting the correct index to '\0'
.
Then, compile and run the code again to make sure it does the right thing this time.
Arrays of Strings!
Finally, we’ll implement string_blanks
which requires allocating both an array and the elements inside it.
char **string_blanks(char *s)
Returns a new array of strings where each string has a _
in place of one of the characters. Does not edit the original string.
For example, the input string “Adam” would return {"_dam", "A_am", "Ad_m", "Ada_"}
.
Recall that, in Java, if I allocate an array like so:
String[] strings = new String[10];
The only thing it does is create 10 spots for String
s. If I want to actually allocate the String
s, I need to separately make and assign them. C is no different.
To create an array of strings, we use the type char **
which means “an array of char *
’s. We’ll need to allocate the container first, and then allocate each of the individual
strings in the array individually as well. Your code for string_blanks
should have two lines that make calls to malloc
. Again, remember you can use array notation with pointers.
So, array[1][2]
would refer to the third character of the second array
element.
Task 2.
Write the string_blanks
function. Then, compile, run, and check your results using the following commands.
To do this on OS X, run the commands:
clang -Wno-nullability-completeness task2.c -o task2
./task2
To do this on Windows, run the commands:
cl.exe -W3 task2.c
./task2.exe
Getting Checked Off
Lab \(n\) will be due during lab hours for lab \(n+1\). You will need to get manually checked off for most labs as we will not be using totally automated tests. To get checked off, join the office hours queue, and we will get to you as soon as possible.