|
1 | 1 | # C-programming-pre-lab |
| 2 | + |
2 | 3 | Pre-lab to get started on compiling and running C programs and valgrind |
| 4 | + |
| 5 | +- [Background](#background) |
| 6 | + - [Checking vs. Exploration](#checking-vs-exploration) |
| 7 | + - [Compiling and running a C program](#compiling-and-running-a-c-program) |
| 8 | + - [Using valgrind to find memory leaks](#using-valgrind-to-find-memory-leaks) |
| 9 | +- [Exercise](#exercise) |
| 10 | + |
| 11 | +Background |
| 12 | +---------------------------------------- |
| 13 | + |
| 14 | +This pre-lab is for the _C and memory management_ labs, parts 1 and 2, |
| 15 | +which includes several C programming exercises with an emphasis on arrays, pointers, |
| 16 | +and memory management. The Internet is chock full of C tutorials, etc.; |
| 17 | +some are listed on the |
| 18 | +[CSci3403 Resources Page](https://github.umn.edu/UMM-CSci3403-F15/Resources/wiki), but there are no doubt zillions of good resources out there we've never heard of. |
| 19 | + |
| 20 | +### Checking vs. Exploration |
| 21 | + |
| 22 | +[As this article points out nicely](http://www.developsense.com/2009/08/testing-vs-checking.html), |
| 23 | +it's useful to make distinction between checking (which is what we |
| 24 | +typically call testing in our courses) and exploration (he calls it |
| 25 | +testing, but I prefer exploration given that "testing" means other |
| 26 | +things). Checking is what we do to see if our code (still) works. |
| 27 | +Exploration is what we do to learn more about a domain or a tool or a |
| 28 | +language. Exploration is often crucial when we're new to a space, and |
| 29 | +it's important to recognize that the stuff we're writing when we explore |
| 30 | +is often pretty crappy (because we don't know what we're doing yet). As |
| 31 | +a result one often does the exploring off to the side, with the |
| 32 | +intention of throwing it away. I bring all this up because I suspect |
| 33 | +there will be a fair amount of exploring that goes on during this lab. |
| 34 | +Try to be intentional and honest about that. Step off to the side and |
| 35 | +try a little exploratory code to figure out if you've got an idea worked |
| 36 | +out correctly. Then throw away that "quick and dirty" code, and bring |
| 37 | +your new knowledge back to the project at hand. |
| 38 | + |
| 39 | +### Compiling and running a C program |
| 40 | + |
| 41 | +In the exercise below you'll need to edit, (re)compile, and run the C |
| 42 | +program `check_whitespace.c` that is provided in this repository. |
| 43 | +Assuming you're in the project directory, you can compile this using the |
| 44 | +command |
| 45 | + |
| 46 | +```bash |
| 47 | +gcc -g -Wall -o check_whitespace check_whitespace.c |
| 48 | +``` |
| 49 | + |
| 50 | +`gcc` is the GNU C Compiler, which is pretty much the only C compiler |
| 51 | +people use on Linux boxes these days. The meaning of the flags: |
| 52 | + |
| 53 | +- `-g` tells `gcc` to include debugging information in the generated |
| 54 | + executable. This is allows, for example, programs like `valgrind` |
| 55 | + (described below) to list line numbers where it thinks there are |
| 56 | + problems. Without `-g` it will identify functions, but not have line |
| 57 | + numbers. |
| 58 | +- `-Wall` (it's a capital 'W') is short for "Warnings all" and turns |
| 59 | + on *all* the warnings that `gcc` supports. This is a Very Good Idea |
| 60 | + because there are a ton of crazy things that C will try to |
| 61 | + "understand" for you, and `-Wall` tells the compiler to warn you |
| 62 | + about those things instead of just quietly (mis)interpreting them. |
| 63 | + You should typically use `-Wall` and make sure to figure out and |
| 64 | + clean up any warnings you do get. |
| 65 | +- `-o <name>` tells `gcc` to put the resulting executable in a file |
| 66 | + with the given name. If you don't provide the `-o` flag then `gcc` |
| 67 | + will write the executable to a file called `a.out` for strange |
| 68 | + historical reasons. |
| 69 | + |
| 70 | +Assuming your program compiled correctly (**check the output!**) then you |
| 71 | +should be able to run the program like any other executable: |
| 72 | + |
| 73 | +```{bash} |
| 74 | +./check_whitespace |
| 75 | +``` |
| 76 | + |
| 77 | +### Using valgrind to find memory leaks |
| 78 | + |
| 79 | +One of the more subtle problems with explicit memory management is that |
| 80 | +you can allocate memory that you never free up again when you're done |
| 81 | +with it. This will typically never lead to an error, but can cause a |
| 82 | +long-running process to consume more and more memory over time until its |
| 83 | +performance begins to degrade or it ultimately crashes the system. Since |
| 84 | +system processes (e.g. file servers, authentication servers, and web servers) |
| 85 | +often run for days, weeks, or months |
| 86 | +between restarts, a memory leak in such a program can be quite serious. |
| 87 | +As a simple example, consider the (silly) function: |
| 88 | + |
| 89 | +```C |
| 90 | +void f(char *str) { |
| 91 | + char *c = calloc(100, sizeof(char)); |
| 92 | + /* Do stuff with c */ |
| 93 | + return 0; |
| 94 | + } |
| 95 | +``` |
| 96 | +
|
| 97 | +The problem here is the fact that `f` allocates 100 bytes (100 |
| 98 | +characters) for `c` to point to which are never freed. This means that |
| 99 | +every time we call `f`, 100 bytes will be allocated to this process that |
| 100 | +we'll *never* be able to get back because we have no way of accessing |
| 101 | +that pointer outside of `f`. To fix that problem (assuming we really |
| 102 | +need to allocate that space) we need to free it before we return: |
| 103 | +
|
| 104 | +```C |
| 105 | +void f(char *str) { |
| 106 | + char *c = calloc(100, sizeof(char)); |
| 107 | + /* Do stuff with c */ |
| 108 | + free(c); |
| 109 | + return 0; |
| 110 | + } |
| 111 | +``` |
| 112 | + |
| 113 | +These sorts of memory leaks can actually be really nasty to spot, so |
| 114 | +happily there's a nice program called `valgrind` that can help identify |
| 115 | +them. If your executable is `my_prog`, then running |
| 116 | + |
| 117 | +``` {bash} |
| 118 | +valgrind ./my_prog |
| 119 | +``` |
| 120 | + |
| 121 | +will run the program as normal, and then print out a memory usage/leak |
| 122 | +report at the end. To get more detailed information, including what |
| 123 | +lines generate a leak, |
| 124 | + |
| 125 | +* Make sure to compile your program with the `-g` flag, and |
| 126 | +* Add the `--leak-check=full` flag when running `valgrind`: |
| 127 | + |
| 128 | +```bash |
| 129 | +valgrind --leak-check=full ./my_prog |
| 130 | +``` |
| 131 | + |
| 132 | +This generates lots of output of the form: |
| 133 | + |
| 134 | + ==28587== 18 bytes in 1 blocks are definitely lost in loss record 50 of 50 |
| 135 | + ==28587== at 0x400522F: calloc (vg_replace_malloc.c:418) |
| 136 | + ==28587== by 0x80486AE: str_reverse (palindrome.c:12) |
| 137 | + ==28587== by 0x804870A: palindrome (palindrome.c:27) |
| 138 | + ==28587== by 0x80487FF: not_palindrome (palindrome_test.c:13) |
| 139 | + ==28587== by 0x8048963: test_long_strings (palindrome_test.c:54) |
| 140 | + ==28587== by 0x804A1B8: _run_test (cmockery.c:1519) |
| 141 | + ==28587== by 0x804A5A7: _run_tests (cmockery.c:1624) |
| 142 | + ==28587== by 0x80489B3: main (palindrome_test.c:68) |
| 143 | + |
| 144 | +This tells you that 18 bytes were lost, and that were allocated by |
| 145 | +`calloc` (the top line of the trace), which was called on line 12 of |
| 146 | +`palindrome.c` in the function `str_reverse` (next to top line of the |
| 147 | +trace), etc. Note that this tells you where the lost bytes were |
| 148 | +*allocated*, which doesn't always tell you much about where they should |
| 149 | +be *freed*, as that's going to depend on how they're used after they're |
| 150 | +allocated. |
| 151 | + |
| 152 | +Exercise |
| 153 | +------------------------------------ |
| 154 | + |
| 155 | +1. Compile the program `check_whitespace.c` |
| 156 | +and run `valgrind` on it to find any leaks it may have (hint: it has at |
| 157 | +least one). |
| 158 | +2. In `leak_report.md` describe why the memory errors happen, and how to fix them. |
| 159 | +3. Actually fix the code. |
| 160 | +4. Commit, push, etc. |
0 commit comments