Setup
In my cs61c directory, pull the files for this lab with:
$ git fetch starter main
$ git merge FETCH_HEAD
Exercise 1: Pointers to Stack vs Heap
Edit ex1_ptr_heap_stack.c using your editor of choice and fill in the blanks.
My answer:
#include <stdio.h>
#include <stdlib.h>
int* int_on_stack() {
// Allocates memory on the stack for an integer
int x = 5;
// Returns a pointer that points to the number 5
return &x;
}
int* int_on_heap() {
// TODO: allocate memory on the heap for an integer
int * ptr_to_5 = malloc(sizeof(int));
// TODO: store the number 5 in memory you just allocated
*ptr_to_5 = 5;
// Returns a pointer that points to the number 5
return ptr_to_5;
}
int main() {
int* ptr_to_stack = int_on_stack();
int* ptr_to_heap = int_on_heap();
printf("ptr_to_stack is the address %p\n", ptr_to_stack);
printf("ptr_to_heap is the address %p\n", ptr_to_heap);
return 0;
}
Compile and run the program and check that the output matches what you expect. You will see a compiler warning. What does it mean?
My answer:
$ gcc ex1_ptr_heap_stack.c && ./a.out
ex1_ptr_heap_stack.c: In function ‘int_on_stack’:
ex1_ptr_heap_stack.c:9:10: warning: function returns address of local variable [-Wreturn-local-addr]
9 | return &x;
| ^~
ptr_to_stack is the address (nil)
ptr_to_heap is the address 0x61618d2c92a0
Read the output of gcc. Note that it throws an error about the address of a stack variable being returned. Similarly, the output of the program should show address of the stack variable as (nil), indicating that it is not usable. This is because x cannot be accessed outside of the function int_on_stack using a pointer. In the future, make sure to allocate memory on the heap if you'd like to use it later.
Exercise 2: Compiler Warnings and Errors
Compiler warnings are generated to help you find potential bugs in your code. Make sure that you fix all of your compiler warnings before you attempt to run your code. This will save you a lot of time debugging in the future because fixing the compiler warnings is much faster than trying to find the bug on your own.
- Read over the code in
ex2_compiler_warnings.c. - Compile your program with
gcc -o ex2_compiler_warnings ex2_compiler_warnings.c. You should see 3 warnings. - Read the first line of the first warning. The line begins with
ex2_compiler_warnings.c:13:22, which tells you that the warning is caused by line 13 ofex2_compiler_warnings.c. The warning states that the program is trying to assign acharto achar *. - Open
ex2_compiler_warnings.cand navigate to the line that's causing the warning. It is trying to assign acharto achar *. The compiler has pointed this out as a potential error because we should not be assigning acharto achar *. - Fix this compiler warning.
- Recompile your code. You can now see that this warning does not appear anymore and there are 2 warnings left.
- Fix the remaining compiler warnings in
ex2_compiler_warnings.c.
My answer:
- Compile Warnings:
$ gcc ex2_compiler_warnings.c
ex2_compiler_warnings.c: In function ‘make_course’:
ex2_compiler_warnings.c:13:22: warning: assignment to ‘char *’ from ‘char’ makes pointer from integer without a cast [-Wint-conversion]
13 | new_course->name = *name;
| ^
ex2_compiler_warnings.c:15:12: warning: returning ‘struct Course **’ from a function with incompatible return type ‘struct Course *’ [-Wincompatible-pointer-types]
15 | return &new_course;
| ^~~~~~~~~~~
ex2_compiler_warnings.c:15:12: warning: function returns address of local variable [-Wreturn-local-addr]
- No bug version:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct Course {
int id;
char *name;
};
struct Course *make_course(int id, char *name) {
struct Course *new_course = malloc(sizeof(struct Course));
new_course->id = id;
new_course->name = name;
return new_course;
}
int main() {
struct Course *cs161 = make_course(161, "Computer Security");
printf("Welcome to CS%d: %s!\n", cs161->id, cs161->name);
return 0;
}
Exercise 3: Intro to GDB
In this section, you will learn the GDB commands start, step, next, finish, print, and quit. This section will resolve bug(s) along the way. Make sure to fix the bug(s) in the code before moving on.
The table below is a summary of the above commands
| Command | Abbreviation | Description |
|---|---|---|
| start | N/A | begin running the program and stop at line 1 in main |
| step | s | execute the current line of code (this command will step into functions) |
| next | n | execute the current line of code (this command will not step into functions) |
| finish | fin | executes the remainder of the current function and returns to the calling function |
| print [arg] | p | prints the value of the argument |
| quit | q | exits gdb |
You should be filling in ex3_commands.txt with the corresponding commands. Please only use the commands from the table above. For correctness, we will be checking the output of your ex3_commands.txt against a desired output. We'd recommend opening two SSH windows so you can have the commands file and the cgdb session at the same time. Even though you are adding to ex3_commands.txt, please check your work by actually running these commands in cgdb.
-
Compile your program with the
-gflag. This will include additional debugging information in the executable that CGDB needs.gcc -g -o pwd_checker test_pwd_checker.c pwd_checker.c -
Start
cgdb. Note that you should be using the executable (pwd_checker) as the argument, not the source file (pwd_checker.c).cgdb pwd_checkerYou should now see CGDB open. The top window displays our code and the bottom window displays the console.
For each of the following steps, add the CGDB commands you execute to ex3_commands.txt. Each command should be on its own line. Each step below will require one or more CGDB commands.
- Start your program so that it's at the first line in
main, using one command. - The first line in
mainis a call toprintf. We do not want to step into this function. Step over this line in the program. - Step until the program is on the
check_passwordcall. Note that the line with an arrow next to it is the line we're currently on, but has not been executed yet. - Step into
check_password. - Step into
check_lower. - Print the value of
password(passwordis a string). - Step out of
check_lowerimmediately. Do not step until the function returns. - Step into
check_length. - Step to the last line of the function.
- Print the return value of the function. The return value should be
false. - Print the value of
length. It looks likelengthwas correct, so there must be some logic issue on line 24. - Quit CGDB. CGDB might ask you if you want to quit, type
y(but do not addytoex3_commands.txt).
At this point, your ex3_commands.txt should contain a list of commands from the steps above. You don't need to add anything from the steps below to your ex3_commands.txt.
My answer:
ex3_commands.txt:
# You should be editing this file for exercise 3 of lab 2.
# Make sure each command is on its own line
# Lines starting with # are comments, feel free to add any to document your commands
# Please add your commands below this line
start
n
n
n
n
s
s
p password
fin
n
s
n
n
n
p meets_len_req
p length
q
- GDB output:
$ gdb pwd_checker -q
Reading symbols from pwd_checker...
(gdb) start
Temporary breakpoint 1 at 0x1174: file test_pwd_checker.c, line 6.
Starting program: /home/dsy/Code/cs61c/lab02/pwd_checker
Temporary breakpoint 1, main () at test_pwd_checker.c:6
6 printf("Running tests...\n\n");
(gdb) n
Running tests...
8 const char *test1_first = "Abraham";
(gdb) n
9 const char *test1_last = "Garcia";
(gdb) n
10 const char *test1_pwd = "qrtv?,mp!ltrA0b13rab4ham";
(gdb) n
11 bool test1 = check_password(test1_first, test1_last, test1_pwd);
(gdb) s
check_password (first_name=0x55555555601a "Abraham", last_name=0x555555556022 "Garcia", password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham")
at pwd_checker.c:83
83 lower = check_lower(password);
(gdb) s
check_lower (password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham") at pwd_checker.c:48
48 while (*password != '\0') {
(gdb) p password
$1 = 0x555555556029 "qrtv?,mp!ltrA0b13rab4ham"
(gdb) fin
Run till exit from #0 check_lower (password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham") at pwd_checker.c:48
0x00005555555556b5 in check_password (first_name=0x55555555601a "Abraham", last_name=0x555555556022 "Garcia",
password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham") at pwd_checker.c:83
83 lower = check_lower(password);
Value returned is $2 = true
(gdb) n
84 length = check_length(password);
(gdb) s
check_length (password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham") at pwd_checker.c:23
23 int length = strlen(password);
(gdb) n
24 bool meets_len_req = (length <= 10);
(gdb) n
25 return meets_len_req;
(gdb) n
26 }
(gdb) p meets_len_req
$3 = false
(gdb) p length
$4 = 24
(gdb) q
A debugging session is active.
Inferior 1 [process 30677] will be killed.
Quit anyway? (y or n) y
- Fix the bug on line 24.
- Compile and run your code.
- The program still fails. Open and step through
cgdbagain, you should see thatcheck_numberis now failing. We will address this in the next exercise.
My answer:
- fixed code:
/* Returns true if the length of PASSWORD is at least 10, false otherwise */
bool check_length(const char *password) {
int length = strlen(password);
bool meets_len_req = (length >= 10);
return meets_len_req;
}
- compile and run:
$ gcc -g -o pwd_checker test_pwd_checker.c pwd_checker.c && ./pwd_checker
Running tests...
pwd_checker: test_pwd_checker.c:12: main: Assertion `test1' failed.
[1] 32580 IOT instruction (core dumped) ./pwd_checker
- GDB output:
$ gdb pwd_checker -q
Reading symbols from pwd_checker...
(gdb) start
Temporary breakpoint 1 at 0x1174: file test_pwd_checker.c, line 6.
Starting program: /home/dsy/Code/cs61c/lab02/pwd_checker
Temporary breakpoint 1, main () at test_pwd_checker.c:6
6 printf("Running tests...\n\n");
(gdb) n
Running tests...
8 const char *test1_first = "Abraham";
(gdb) n
9 const char *test1_last = "Garcia";
(gdb) n
10 const char *test1_pwd = "qrtv?,mp!ltrA0b13rab4ham";
(gdb) n
11 bool test1 = check_password(test1_first, test1_last, test1_pwd);
(gdb) s
check_password (first_name=0x55555555601a "Abraham", last_name=0x555555556022 "Garcia", password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham")
at pwd_checker.c:83
83 lower = check_lower(password);
(gdb) n
84 length = check_length(password);
(gdb) n
85 name = check_name(first_name, last_name, password);
(gdb) n
86 number = check_number(password);
(gdb) s
check_number (password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham") at pwd_checker.c:60
60 while (*password != '\0') {
(gdb) n
61 if (check_range(*password, 0, 9)) {
(gdb) n
64 ++password;
(gdb) n
60 while (*password != '\0') {
(gdb) fin
Run till exit from #0 check_number (password=0x55555555602a "rtv?,mp!ltrA0b13rab4ham") at pwd_checker.c:60
0x00005555555556ed in check_password (first_name=0x55555555601a "Abraham", last_name=0x555555556022 "Garcia",
password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham") at pwd_checker.c:86
86 number = check_number(password);
Value returned is $1 = false
(gdb) q
A debugging session is active.
Inferior 1 [process 33118] will be killed.
Quit anyway? (y or n) y
Exercise 4: More GDB
In this section, you will learn the gdb commands break, conditional break, run, and continue. This section will resolve bug(s) along the way. Make sure to fix the bug(s) in the code before moving on.
The table below is a summary of the above commands
| Command | Abbreviation | Description |
|---|---|---|
| break [line num or function name] | b | set a breakpoint at the specified location, use filename.c:linenum to set a breakpoint in a specific file |
| conditional break (ex: break 3 if n==4) | (ex: b 3 if n==4) | set a breakpoint at the specified location only if a given condition is met |
| run | r | execute the program until termination or reaching a breakpoint |
| continue | c | continues the execution of a program that was paused |
You should be filling in ex4_commands.txt with the corresponding commands. Please only use the commands from the table above and the table for exercise 2. For correctness, we will be checking the output of your ex4_commands.txt against a desired output. We'd recommend opening two SSH windows so you can have the commands file and the cgdb session at the same time. Even though you are adding to ex4_commands.txt, please check your work by actually running these commands in cgdb.
-
Recompile and run your code. You should see that the assertion
numberis failing -
Start cgdb
cgdb pwd_checker
For each of the following steps, add the CGDB commands you execute to ex4_commands.txt. Each command should be on its own line. Each step below will require one or more CGDB commands.
-
Set a breakpoint in our code to jump straight into the function
check_numberusing the function name (not the filename or line number). Your breakpoint should not be incheck_password. -
Run the program. Your code should run until it gets to the breakpoint that we just set.
-
Step into
check_range. -
Recall that the numbers do not appear until later in the password. Instead of stepping through all of the non-numerical characters at the beginning of password, we can jump straight to the point in the code where the numbers are being compared using a conditional breakpoint. A conditional breakpoint will only stop the program based on a given condition. The first number in the password
0, so we can set the breakpoint whenletteris'0'. Break on line 31 if theletteris'0'.We are using the single quote because
0is a char. -
Continue executing your code after it stops at a breakpoint.
-
The code has stopped at the conditional breakpoint. To verify this, print
letter.It should print
48 '0'which is a decimal number followed by it's corresponding ASCII representation. If you look at an ASCII table, you can see that48is the decimal representation of the character0. -
Let's take a look at the return value of
check_range. Printis_in_range. The result isfalse. That's strange.'0'should be in the range. -
Let's look at the upper and lower bounds of the range. Print
lower. -
Print
upper. -
Ahah! The ASCII representation of
loweris\000(the null terminator) and the ASCII representation ofupperis\t. It looks like we passed in the numbers0and9instead of the characters'0'and'9'! -
Quit CGDB. CGDB might ask you if you want to quit, type
y(but do not addytoex4_commands.txt).
At this point, your ex4_commands.txt should contain a list of commands from the steps above. You don't need to add anything from the steps below to your ex4_commands.txt.
My answer:
ex4_commands.txt:
# You should be editing this file for exercise 4 of lab 2.
# Make sure each command is on its own line
# Lines starting with # are comments, feel free to add any to document your commands
# Please add your commands below this line
b check_number
r
n
s
b 31 if letter == '0'
c
p is_in_range
p lower
p upper
q
- GDB output:
$ gdb pwd_checker -q
Reading symbols from pwd_checker...
(gdb) b check_number
Breakpoint 1 at 0x15f5: file pwd_checker.c, line 60.
(gdb) r
Starting program: /home/dsy/Code/cs61c/lab02/pwd_checker
Breakpoint 1, check_number (password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham") at pwd_checker.c:60
60 while (*password != '\0') {
(gdb) n
61 if (check_range(*password, 0, 9)) {
(gdb) s
check_range (letter=113 'q', lower=0 '\000', upper=9 '\t') at pwd_checker.c:30
30 bool is_in_range = (letter > lower && letter < upper);
(gdb) b 31 if letter == '0'
Breakpoint 2 at 0x555555555543: file pwd_checker.c, line 31.
(gdb) c
Continuing.
Breakpoint 2, check_range (letter=48 '0', lower=0 '\000', upper=9 '\t') at pwd_checker.c:31
31 return is_in_range;
(gdb) p is_in_range
$1 = false
(gdb) p lower
$2 = 0 '\000'
(gdb) p upper
$3 = 9 '\t'
(gdb) q
A debugging session is active.
Inferior 1 [process 33886] will be killed.
Quit anyway? (y or n) y
- Fix the bug.
- Compile and run your code. There's one more error, which you will find in exercise 5.
My answer:
- fixed code:
/* Returns true if PASSWORD contains at least one number, false otherwise */
bool check_number(const char *password) {
while (*password != '\0') {
if (check_range(*password, '0', '9')) {
return true;
}
++password;
}
return false;
}
- compile and run:
$ gcc -g -o pwd_checker test_pwd_checker.c pwd_checker.c && ./pwd_checker
Running tests...
pwd_checker: test_pwd_checker.c:12: main: Assertion `test1' failed.
[1] 34650 IOT instruction (core dumped) ./pwd_checker
Exercise 5: Debug
- Debug
check_upperon your own using the commands you just learned. The function appears to be returningfalseeven though there's an uppercase letter. Hint: the bug itself may not be incheck_upperitself.
My answer:
- GDB output:
$ gdb pwd_checker -q
Reading symbols from pwd_checker...
(gdb) b check_upper
Breakpoint 1 at 0x1555: file pwd_checker.c, line 36.
(gdb) r
Starting program: /home/dsy/Code/cs61c/lab02/pwd_checker
Breakpoint 1, check_upper (password=0x555555556029 "qrtv?,mp!ltrA0b13rab4ham") at pwd_checker.c:36
36 while (*password != '\0') {
(gdb) n
37 bool is_in_range = check_range(*password, 'A', 'Z');
(gdb) s
check_range (letter=113 'q', lower=65 'A', upper=90 'Z') at pwd_checker.c:30
30 bool is_in_range = (letter > lower && letter < upper);
(gdb) b 31 if (letter >= 'A' && letter <= 'Z')
Breakpoint 2 at 0x555555555543: file pwd_checker.c, line 31.
(gdb) c
Continuing.
Breakpoint 2, check_range (letter=65 'A', lower=65 'A', upper=90 'Z') at pwd_checker.c:31
31 return is_in_range;
(gdb) p is_in_range
$1 = false
(gdb) fin
Run till exit from #0 check_range (letter=65 'A', lower=65 'A', upper=90 'Z') at pwd_checker.c:31
0x0000555555555572 in check_upper (password=0x555555556035 "A0b13rab4ham") at pwd_checker.c:37
37 bool is_in_range = check_range(*password, 'A', 'Z');
Value returned is $2 = false
(gdb) n
38 if (is_in_range) {
(gdb) n
41 ++password;
(gdb) n
36 while (*password != '\0') {
(gdb) n
37 bool is_in_range = check_range(*password, 'A', 'Z');
(gdb) q
A debugging session is active.
Inferior 1 [process 34971] will be killed.
Quit anyway? (y or n) y
- fixed code:
/* Returns true if LETTER is in the range [LOWER, UPPER], false otherwise */
bool check_range(char letter, char lower, char upper) {
bool is_in_range = (letter >= lower && letter <= upper);
return is_in_range;
}
- compile and run:
$ gcc -g -o pwd_checker test_pwd_checker.c pwd_checker.c && ./pwd_checker
Running tests...
Congrats! You have passed all of the test cases!
Exercise 6: Using Valgrind to find segfaults
There's a bug in ex6_valgrind, let's see how we can detect it with valgrind.
-
Compile
ex6_valgrind.c. Notice that there are no compiler errors or warnings, and we're using the-gflag in case we need to debug this program in the future.gcc -g -o ex6_valgrind ex6_valgrind.c -
Run
ex6_valgrind. Notice that the program doesn't throw any errors. -
Run
valgrindonex6_valgrind. You should see that there are 2 errors. -
Read the valgrind output carefully. In
ex6_answers.txt, answer the following questions. Please don't change the formatting of the file. For question 1 through 7, we are referring to the firstvalgrinderror (an invalid write error).- How many bytes are the invalid write? (The answer should be a number without any units)
- Which function caused the invalid write? (The answer should be the name of the function)
- Which function called the answer to question 2? (The answer should be the name of a function)
- Which file did the call occur in? (The answer should be the name of a file)
- Which line did the call occur on? (The answer should be a number)
- How many bytes were actually allocated? (The answer should be a number without any units)
- How many bytes should have been allocated? Feel free to read the code. (The answer should be a number without any units)
- Are there any memory leaks? (The answer should be Yes or No)
- How many bytes were leaked? Write 0 if there are no memory leaks. (The answer should be a number without any units)
My answer:
- valgrind output:
$ valgrind ./ex6_valgrind
==26527== Memcheck, a memory error detector
==26527== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==26527== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==26527== Command: ./ex6_valgrind
==26527==
==26527== Invalid write of size 1
==26527== at 0x484C3DE: strcpy (vg_replace_strmem.c:561)
==26527== by 0x10919F: copy_str (ex6_valgrind.c:7)
==26527== by 0x1091BC: main (ex6_valgrind.c:12)
==26527== Address 0x4a7504c is 0 bytes after a block of size 12 alloc'd
==26527== at 0x4843788: malloc (vg_replace_malloc.c:442)
==26527== by 0x109188: copy_str (ex6_valgrind.c:6)
==26527== by 0x1091BC: main (ex6_valgrind.c:12)
==26527==
==26527== Invalid read of size 1
==26527== at 0x484C274: strlen (vg_replace_strmem.c:502)
==26527== by 0x4908687: puts (ioputs.c:35)
==26527== by 0x1091CC: main (ex6_valgrind.c:13)
==26527== Address 0x4a7504c is 0 bytes after a block of size 12 alloc'd
==26527== at 0x4843788: malloc (vg_replace_malloc.c:442)
==26527== by 0x109188: copy_str (ex6_valgrind.c:6)
==26527== by 0x1091BC: main (ex6_valgrind.c:12)
==26527==
hello world!
==26527==
==26527== HEAP SUMMARY:
==26527== in use at exit: 12 bytes in 1 blocks
==26527== total heap usage: 2 allocs, 1 frees, 1,036 bytes allocated
==26527==
==26527== LEAK SUMMARY:
==26527== definitely lost: 12 bytes in 1 blocks
==26527== indirectly lost: 0 bytes in 0 blocks
==26527== possibly lost: 0 bytes in 0 blocks
==26527== still reachable: 0 bytes in 0 blocks
==26527== suppressed: 0 bytes in 0 blocks
==26527== Rerun with --leak-check=full to see details of leaked memory
==26527==
==26527== For lists of detected and suppressed errors, rerun with: -s
==26527== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
- ex6_answers.txt
1. 1
2. strcpy
3. copy_str
4. vg_replace_strmem.c
5. 561
6. 1,036
7. 13
8. Yes
9. 12
Exercise 7: Memory Management
This exercise uses ex7_vector.h, ex7_test_vector.c, and ex7_vector.c, where we provide you with a framework for implementing a variable-length array. This exercise is designed to help familiarize you with C structs and memory management in C.
-
Try to explain why
bad_vector_new()is bad. We have provided the reason here, so you can verify your understandingThe vector is created on the stack, instead of the heap. All memory stored on the stack gets freed as soon as that function finishes running, so when the function returns, we lose the vector we constructed.
-
Fill in the functions
vector_new(),vector_get(),vector_delete(), andvector_set()inex7_vector.cso that our test codeex7_test_vector.cruns without any memory management errors.Comments in the code describe how the functions should work. Look at the functions we've filled in to see how the data structures should be used. For consistency, it is assumed that all entries in the vector are 0 unless set by the user. Keep this in mind as
malloc()does not zero out the memory it allocates.vector_setshould resize the array if the index passed in is larger than the size of the array. -
Test your implementation of
vector_new(),vector_get(),vector_delete(), andvector_set()for correctness.gcc -g -o ex7_vector ex7_vector.c ex7_test_vector.c ./ex7_vector -
Test your implementation of
vector_new(),vector_get(),vector_delete(), andvector_set()for memory management.valgrind ./ex7_vector
Any number of suppressed errors is fine; they do not affect us.
Feel free to also use CGDB to debug your code.
My answer:
- ex7_vector.c
/* Create a new vector with a size (length) of 1 and set its single component to zero... the
right way */
/* TODO: uncomment the code that is preceded by // */
vector_t *vector_new()
{
/* Declare what this function will return */
vector_t *retval;
/* First, we need to allocate memory on the heap for the struct */
retval = malloc(sizeof(vector_t));
/* Check our return value to make sure we got memory */
if (retval == NULL)
{
allocation_failed();
}
/* Now we need to initialize our data.
Since retval->data should be able to dynamically grow,
what do you need to do? */
retval->size = 1;
retval->data = malloc(sizeof(int));
/* Check the data attribute of our vector to make sure we got memory */
if (retval->data == NULL) {
free(retval); //Why is this line necessary?
allocation_failed();
}
/* Complete the initialization by setting the single component to zero */
*(retval->data) = 0;
/* and return... */
return retval; /* UPDATE RETURN VALUE */
}
/* Return the value at the specified location/component "loc" of the vector */
int vector_get(vector_t *v, size_t loc)
{
/* If we are passed a NULL pointer for our vector, complain about it and exit. */
if (v == NULL)
{
fprintf(stderr, "vector_get: passed a NULL vector.\n");
abort();
}
/* If the requested location is higher than we have allocated, return 0.
* Otherwise, return what is in the passed location.
*/
/* YOUR CODE HERE */
if (loc < v->size) {
return v->data[loc];
}
return 0;
}
/* Free up the memory allocated for the passed vector.
Remember, you need to free up ALL the memory that was allocated. */
void vector_delete(vector_t *v)
{
/* YOUR CODE HERE */
free(v->data);
free(v);
}
/* Set a value in the vector, allocating additional memory if necessary.
If the extra memory allocation fails, call allocation_failed(). */
void vector_set(vector_t *v, size_t loc, int value)
{
/* What do you need to do if the location is greater than the size we have
* allocated? Remember that unset locations should contain a value of 0.
*/
/* YOUR CODE HERE */
if (loc < v->size) {
v->data[loc] = value;
} else {
int new_size = loc + 1;
int *new_data = malloc(new_size * sizeof(int));
if (new_data == NULL) {
allocation_failed();
}
for (int i = 0; i < loc; i++) {
if (i < v->size) {
new_data[i] = v->data[i];
} else {
new_data[i] = 0;
}
}
new_data[loc] = value;
free(v->data);
v->size = new_size;
v->data = new_data;
}
}
Exercise 8: Double Pointers
Edit ex8_double_pointers.c using your editor of choice and fill in the blanks.
Compile and run the program and check that the output matches what you expect.
My answer:
- ex8_double_pointers.c
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int id;
char* name;
} Student;
Student* create_student_1(int id) {
// TODO: allocate memory to store a Student struct
Student * student_ptr = malloc(sizeof(Student));
// TODO: set student_ptr's id to the id argument of this function
student_ptr->id = id;
return student_ptr;
}
void create_student_2(Student** student_double_ptr, int id) {
// TODO: fill the space that student_double_ptr points to with the address of
// some memory large enough for a Student struct
// Hint: you may need to use the dereference operator here
*student_double_ptr = malloc(sizeof(Student));
// TODO: set student_double_ptr's id to the id argument of this function
(*student_double_ptr)->id = id;
}
int main() {
// TODO: use create_student_1 to create a pointer to a Student struct
// where the student has id of 5
Student * student1_ptr = create_student_1(5);
// TODO: print the id of the student that student1_ptr points to
printf("Student 1's ID: %d\n", student1_ptr->id);
// TODO: create a pointer that can point to a Student struct
// do not allocate any memory
Student * student2_ptr;
// TODO: use create_student_2 to populate the student2_ptr
// where the student has id of 6
// Hint: compare the type of student2_ptr with the type of
// the argument for create_student_2
create_student_2(&student2_ptr, 6);
// TODO: print the id of the student that student2_ptr points to
printf("Student 2's ID: %d\n", student2_ptr->id);
// Free everything allocated with `malloc`
free(student1_ptr);
free(student2_ptr);
return 0;
}
Exercise 9: Putting It All Together
Here's one to help you in your interviews. In ex9_cycle.c, complete the function ll_has_cycle() to implement the following algorithm for checking if a singly-linked list has a cycle.
- Start with two pointers at the head of the list. One will be called fast_ptr and the other will be called slow_ptr.
- Advance fast_ptr by two nodes. If this is not possible because of a null pointer, we have found the end of the list, and therefore the list is acyclic.
- Advance slow_ptr by one node. (A null pointer check is unnecessary. Why?)
- If the fast_ptr and slow_ptr ever point to the same node, the list is cyclic. Otherwise, go back to step 2.
- If the list has a cycle, return 1. Else, return 0.
If you want to see the definition of the node struct, open ex9_cycle.h.
Action Item
Implement ll_has_cycle(). Once you've done so, you can execute the following commands to run the tests for your code. If you make any changes, make sure to run ALL of the following commands again, in order.
gcc -g -o ex9_test_cycle ex9_test_cycle.c ex9_cycle.c
./ex9_test_cycle
Here's a Wikipedia article on the algorithm and why it works. Don't worry about it if you don't completely understand it.
My answer:
#include <stddef.h>
#include "ex9_cycle.h"
int ll_has_cycle(node *head)
{
/* TODO: Implement ll_has_cycle */
node *fast_ptr = head, *slow_ptr = head;
while (fast_ptr != NULL && fast_ptr->next != NULL)
{
fast_ptr = fast_ptr->next->next;
slow_ptr = slow_ptr->next;
if (fast_ptr == slow_ptr)
{
return 1;
}
}
return 0;
}