Exercise 1: Hello!
Edit ex1_hello.c with your editor of choice and make the program print out the string "Hello 61C" instead of "Hello World". Make sure to save your edited file, but do NOT recompile yet.
My answer:
#include <stdio.h>
int main() {
printf("Hello 61C\n");
return 0;
}
Run the executable with ./ex1_hello. You should still see Hello World, which is the old output.
gcc -o ex1_hello ex1_hello.c
./ex1_hello
Hello World
Now, recompile your program with gcc -o ex1_hello ex1_hello.c, and then run the executable with ./ex1_hello again. You should now see Hello 61C.
My answer:
gcc -o ex1_hello ex1_hello.c
./ex1_hello
Hello 61C
Exercise 2: Pointer Basics
Edit ex2_pointer_basics.c using your editor of choice and fill in the blanks.
My answer:
#include <stdio.h>
int main() {
// Assign x (an integer) to 5
int x = 5;
// TODO: create a pointer to x
// Hint: the first blank should be a variable type
// the second blank should be the address of x
int * pointer_to_x = &x;
// This line should print 5
printf("%d\n", *pointer_to_x);
return 0;
}
Compile and run the program and check that the output matches what you expect.
My answer:
$ gcc ex2_pointer_basics.c
$ ./a.out
5
Exercise 3: Pointers and Functions
Edit ex3_pointers_and_functions.c using your editor of choice and fill in the blanks.
My answer:
#include <stdio.h>
void add_one(int input) {
input += 1;
}
void add_one_pointer(int* input) {
// TODO: add one to the integer that input points to
*input += 1;
}
int main() {
// Assign x (an integer) to 5
int x = 5;
// Call add_one on x
add_one(x);
// This line should print 5
// Why doesn't this work?
printf("add_one: %d\n", x);
// Let's try using add_one_pointer
// TODO: use add_one_pointer to increment x
// Hint: compare the type of x with the type of the argument
// for add_one_pointer
add_one_pointer(&x);
// This line should print 6
printf("add_one_pointer: %d\n", x);
return 0;
}
Compile and run the program and check that the output matches what you expect.
My answer:
$ gcc ex3_pointers_and_functions.c
$ ./a.out
add_one: 5
add_one_pointer: 6
Exercise 4: Arrays
Edit ex4_arrays_updated.c using your editor of choice and fill in the blanks.
My answer:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
// Creates an integer with value 5
// Note: int8_t is a numerical datatype that takes up 1 byte of memory
int8_t x = 5;
// TODO: allocate an int8_t array of size 4
int8_t some_array[4];
printf("address of the start of the array: %p\n", some_array);
// TODO: compute the address of the element at index 2 (0-indexed)
int8_t * ptr_to_idx_2 = some_array + 2;
printf("address of index 2: %p\n", ptr_to_idx_2);
// TODO: store the value 10 at index 2, using ptr_to_idx_2
*ptr_to_idx_2 = 10;
// TODO: print the value at index 2
// Hint: this blank should be the same as the previous blank
// please don't hard code 10
printf("value at index 2: %d\n", some_array[2]);
return 0;
}
Compile and run the program and check that the output matches what you expect.
My answer:
$ gcc ex4_arrays_updated.c
$ ./a.out
address of the start of the array: 0x7ffc1712eaa4
address of index 2: 0x7ffc1712eaa6
value at index 2: 10
Read the output of your program. Note that the relationship between the address of the start of the array and the address of index 2 (hint: they're two bytes apart).
Exercise 5: Pointer Arithmetic
Edit ex5_pointer_arithmetic_updated.c using your editor of choice and fill in the blanks.
My answer:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
// Creates an integer with value 5
// Note: int16_t is a numerical datatype that takes up 2 bytes of memory
int16_t x = 5;
// TODO: allocate an int16_t array of size 4
int16_t some_array[4];
printf("address of the start of the array: %p\n", some_array);
// TODO: compute the address of the element at index 2 (0-indexed)
int16_t * ptr_to_idx_2 = some_array + 2;
printf("address of index 2: %p\n", ptr_to_idx_2);
// TODO: store the value 10 at index 2, using ptr_to_idx_2
*ptr_to_idx_2 = 10;
// TODO: print the value at index 2
// Hint: this blank should be the same as the previous blank
// please don't hard code 10
printf("value at index 2: %d\n", some_array[2]);
return 0;
}
Compile and run the program and check that the output matches what you expect.
My answer:
$ gcc ex5_pointer_arithmetic_updated.c && ./a.out
address of the start of the array: 0x7ffd584ad740
address of index 2: 0x7ffd584ad744
value at index 2: 10
Read the output of your program. Note that the relationship between the address of the start of the array and the address of index 2 is different from the relationship in the previous exercise.
Exercise 6: Strings
Edit ex6_strings.c using your editor of choice and fill in the blanks.
My answer:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// TODO: allocate memory to store the string "hello"
// Hint: how many bytes do we need to store this string?
char hello_str[10];
// TODO: store the characters one at a time
// Hint: don't forget the null terminator
// Note: we use single quotes for characters
hello_str[0] = 'h';
hello_str[1] = 'e';
hello_str[2] = 'l';
hello_str[3] = 'l';
hello_str[4] = 'o';
// TODO: store the null terminator
hello_str[5] = '\0';
// Prints hello_str
printf("prints hello: %s\n", hello_str);
// TODO: print the length of hello_str
printf("length of hello: %lu\n", strlen(hello_str));
// TODO: allocate memory to store the string "world"
char world_str[10];
// TODO: fill in the type
// Note: this automatically stores the string "world" in static memory
// but static memory is immutable, so you may need to copy it
// to the stack or the heap
char * static_world_str = "world";
// TODO: use strcpy and static_world_str to store "world" into world_str
// Hint: strcpy takes two arguments:
// first the destination, then the source
strcpy(world_str, static_world_str);
// Prints world_str
printf("prints world: %s\n", world_str);
// Prints the address of world_str
printf("address of world_str: %p\n", world_str);
// TODO: compute the address of the letter r using world_str
char * ptr_to_r = &world_str[2];
printf("address of 'r': %p\n", ptr_to_r);
// TODO: allocate memory to store the string "hello world"
char hello_world_str[20];
// TODO: use strcpy and hello_str to store
// the string "hello" into hello_world_str
strcpy(hello_world_str, hello_str);
// TODO: store the space character in "hello world" at the correct index
// Note: a space is not the same as null terminator
// a null terminator is represented by '\0'
hello_world_str[5] = ' ';
// TODO: use strcpy, pointer arithmetic, and world_str to store
// the string "world" into hello_world_str
strcpy(hello_world_str + 6, world_str);
// Prints hello_world_str
printf("prints hello world: %s\n", hello_world_str);
return 0;
}
Compile and run the program and check that the output matches what you expect.
My answer:
$ gcc ex6_strings.c && ./a.out
prints hello: hello
length of hello: 5
prints world: world
address of world_str: 0x7ffd6e7d2376
address of 'r': 0x7ffd6e7d2378
prints hello world: hello world
Exercise 7: Copying Strings
Edit ex7_strcpy.c using your editor of choice and fill in the blanks. When copying the string, use strcpy (documentation).
My answer:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// TODO: Allocate memory to store the string "hello"
// You may use your solution from a previous exercise;
char message[20];
message[0] = 'h';
message[1] = 'l';
message[2] = 'e';
message[3] = 'l';
message[4] = 'o';
message[5] = '\0';
// Print out the value before we change message
printf("Before copying: %s\n", message);
// Creates another_string that contains a longer string
char* long_message = "Here's a really long string";
// TODO: Copy the string in long_message to message
strcpy(message, long_message);
// Print out the value after we change message
printf("After copying: %s\n", message);
return 0;
}
Compile and run the program. Notice that the program crashes, why?
My answer:
$ gcc ex7_strcpy.c && ./a.out
ex7_strcpy.c: In function ‘main’:
ex7_strcpy.c:23:3: warning: ‘strcpy’ writing 28 bytes into a region of size 20 overflows the destination [-Wstringop-overflow=]
23 | strcpy(message, long_message);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ex7_strcpy.c:8:8: note: destination object ‘message’ of size 20
8 | char message[20];
| ^~~~~~~
Before copying: hlelo
After copying: Here's a really long string
*** stack smashing detected ***: terminated
[1] 46324 IOT instruction (core dumped) ./a.out
strcpyis an unsafe function. We only allocate enough space inmessageto store the messagehello, but then we try to store a longer message. When this happens, we overflow the space we allocated formessage, and crash the program!- To prevent this, we can use
strncpy(documentation) so that we don't overflow the space.
Fix the program in ex7_strcpy_fixed.c so that it stores as many characters of longer_message as you can in message without changing the size of message.
My answer:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// TODO: Allocate memory to store the string "hello"
// You may use your solution from a previous exercise;
char message[20];
message[0] = 'h';
message[1] = 'l';
message[2] = 'e';
message[3] = 'l';
message[4] = 'o';
message[5] = '\0';
// Print out the value before we change message
printf("Before copying: %s\n", message);
// Creates another_string that contains a longer string
char* long_message = "Here's a really long string";
// TODO: Copy the string in long_message to message
strncpy(message, long_message, sizeof(message) / sizeof(char) - 1);
// Print out the value after we change message
printf("After copying: %s\n", message);
return 0;
}
Compile and run the program again and check that the output matches what you expect.
My answer:
$ gcc ex7_strcpy_fixed.c && ./a.out
Before copying: hlelo
After copying: Here's a really lon
Exercise 8: Structs
Edit ex8_structs.c using your editor of choice and fill in the blanks.
My answer:
#include <stdio.h>
#include <stdlib.h>
// Structs allow you to hold data items of different types in a single variable
// Struct definitions can be used to declare a struct variable within your program
// Struct definitions are typically done outside of a function
struct Student {
int id;
char* name;
};
int main() {
// TODO: declare a variable student of type struct Student
// Note: this struct is stored on the stack
struct Student student;
// TODO: print out the size of a struct Student
// While this may seem out of place now, it will be useful in the future!
// Hint: there's an operator that can calculate this for you!
printf("Size of a struct Student: %lu bytes\n", sizeof(struct Student));
// TODO: set student's id field to 5
// Hint: the dot notation accesses a struct's fields
student.id = 5;
// TODO: print out student's id field
printf("Student's ID: %d\n", student.id);
return 0;
}
Compile and run the program and check that the output matches what you expect.
$ gcc ex8_structs.c && ./a.out
Size of a struct Student: 16 bytes
Student's ID: 5
Optional: typedefs
Sometimes, you may see a typedef when declaring a struct:
typedef struct {
int id;
} Student;
In these cases, you may use Student as the type instead of struct Student.
My answer:
#include <stdio.h>
#include <stdlib.h>
// Structs allow you to hold data items of different types in a single variable
// Struct definitions can be used to declare a struct variable within your program
// Struct definitions are typically done outside of a function
typedef struct {
int id;
char* name;
} Student;
int main() {
// TODO: declare a variable student of type struct Student
// Note: this struct is stored on the stack
Student student;
// TODO: print out the size of a struct Student
// While this may seem out of place now, it will be useful in the future!
// Hint: there's an operator that can calculate this for you!
printf("Size of a struct Student: %lu bytes\n", sizeof(Student));
// TODO: set student's id field to 5
// Hint: the dot notation accesses a struct's fields
student.id = 5;
// TODO: print out student's id field
printf("Student's ID: %d\n", student.id);
return 0;
}