Download the files for Lab 2 first.
tar
command, initialize a git repository in the lab2 directory and make your first commit.
vector.h
and vector.c
vector.h
are declared as follows:
Vector *vector_create(void);
void vector_push(Vector *vector, double element);
double vector_get(Vector *vector, int index);
void vector_free(Vector *vector);
malloc()
, realloc()
, and free()
do.
malloc()
and realloc()
fails, do necessary changes to make code work under these circumstances.Vector *vector
passed to it is valid, modify the code to add a null check at the beginning of each function.vector_get()
, it is possible that the user visits a position that is not initialized, you should handle this case.void another_vector_free(Vector **vector)
, add this to your code. Explain why this could be beneficial.Makefile
we've provided.
A debugger, as the name suggests, is a program which is designed specifically to help you find bugs, or logical errors and mistakes in your code (side note: if you want to know why errors are called bugs, look here). Different debuggers have different features, but it is common for all debuggers to be able to do the following things:
For this exercise, you will find the GDB reference card useful. GDB stands for "GNU De-Bugger." :) Use the code you've written in the previous exercise to try the debugger.
This causes gcc to store information in the executable program for
gdb
to make sense of it. Now start our debugger, (c)gdb:
$ cgdb ./lab2
ACTION ITEM: step through the whole program by doing the following:
Type help from within gdb to find out the commands to do these things, or use the reference card.
Look here if you see an error message like printf.c: No such file or directory. You probably stepped into a printf function! If you keep stepping, you'll feel like you're going nowhere! GDB is complaining because you don't have the actual file where printf is defined. This is pretty annoying. To free yourself from this black hole, use the command finish to run the program until the current frame returns (in this case, until printf is finished). And NEXT time, use next to skip over the line which used printf.
ACTION ITEM: Learn MORE gdb commands Learning these commands will prove useful for the rest of this lab, and your C programming career in general. Create a text file containing answers to the following questions (or write them down on a piece of paper, or just memorize them if you think you want to become a GDB pro).
sudo apt install valgrind
(in Ubuntu) for minimal installation.
// File name: main.c #include <stdio.h> int main() { int a = 42; printf("%d\n", a); return 0; }We can easily point out that this program with output
42
is memory safe.
$ valgrind --tool=memcheck --leak-check=full --track-origins=yes ./mainHere's what
Valgrind
gives us, which indicate that no memleaks are detected.
==371318== HEAP SUMMARY: ==371318== in use at exit: 0 bytes in 0 blocks ==371318== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated ==371318== ==371318== All heap blocks were freed -- no leaks are possible ==371318== ==371318== For lists of detected and suppressed errors, rerun with: -s ==371318== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)When there are memleaks:
// File name: memleak.c #include <stdio.h> #include <stdlib.h> int main() { int a = 42; char *normal = malloc(135); char *leak = malloc(246); leak[0] = 'q'; // Avoid unused variable warning printf("%d\n", a); free(normal); return 0; }In this example, we allocated memory from heap for variable
leak
, but the call to free()
for the variable is missing, causing us to fail Gradescope test :(
gcc -Wpedantic -Wall -Wextra -Wvla -Werror memleak.c -o memleak
to make sure there are no warnings, which means that compiler (gcc
) is unable to find any
potential
issues.
Valgrind
to detect the issue:
$ valgrind --tool=memcheck --leak-check=full --track-origins=yes ./memleak
==372058== HEAP SUMMARY: ==372058== in use at exit: 246 bytes in 1 blocks ==372058== total heap usage: 3 allocs, 2 frees, 1,405 bytes allocated ==372058== ==372058== 246 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==372058== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==372058== by 0x1091B3: main (in /home/caoster/Desktop/temp/memleak) ==372058== ==372058== LEAK SUMMARY: ==372058== definitely lost: 246 bytes in 1 blocks ==372058== indirectly lost: 0 bytes in 0 blocks ==372058== possibly lost: 0 bytes in 0 blocks ==372058== still reachable: 0 bytes in 0 blocks ==372058== suppressed: 0 bytes in 0 blocks ==372058== ==372058== For lists of detected and suppressed errors, rerun with: -s ==372058== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)We can tell from above that:
main
with malloc()
.Valgrind
, there are absolutely more messages to reveal.
What's more:
#include <stdio.h> int main() { int a[5] = {13, 24, 35, 46, 57}; printf("%d\n", a[5]); return 0; }With
Valgrind
, we can find those uninitialized visits effortlessly (only a fraction of the message is shown here):
==373455== Conditional jump or move depends on uninitialised value(s) ==373455== at 0x48EFB56: __vfprintf_internal (vfprintf-internal.c:1516) ==373455== by 0x48D981E: printf (printf.c:33) ==373455== by 0x1091BF: main (in /home/caoster/Desktop/temp/main) ==373455== Uninitialised value was created by a stack allocation ==373455== at 0x109169: main (in /home/caoster/Desktop/temp/main)
Valgrind
and the messages it gives you.