Skip to content

Commit 97ed928

Browse files
committed
Merge branch 'release/v1.0'
2 parents d8cb1a1 + 62a3c5c commit 97ed928

File tree

4 files changed

+260
-0
lines changed

4 files changed

+260
-0
lines changed

Credits.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Credits
2+
3+
This pre-lab was initially conceived by Vincent Borchardt, KK Lamberty, and Nic McPhee, in August and September, 2012. Most of the initial implementation was by Vincent, with subsequent editing and updates was provided by Peter Dolan, KK Lamberty, and Nic McPhee.

README.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,160 @@
11
# C-programming-pre-lab
2+
23
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.

check_whitespace.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
#include <stdlib.h>
4+
5+
/*
6+
* Strips spaces from both the front and back of a string,
7+
* leaving any internal spaces alone.
8+
*/
9+
char* strip(char* str) {
10+
int size;
11+
int num_spaces;
12+
int first_non_space, last_non_space, i;
13+
char* result;
14+
15+
size = strlen(str);
16+
17+
// This counts the number of leading and trailing spaces
18+
// so we can figure out how big the result array should be.
19+
num_spaces = 0;
20+
first_non_space = 0;
21+
while (first_non_space<size && str[first_non_space] == ' ') {
22+
++num_spaces;
23+
++first_non_space;
24+
}
25+
26+
last_non_space = size-1;
27+
while (last_non_space>=0 && str[last_non_space] == ' ') {
28+
++num_spaces;
29+
--last_non_space;
30+
}
31+
32+
// If num_spaces >= size then that means that the string
33+
// consisted of nothing but spaces, so we'll return the
34+
// empty string.
35+
if (num_spaces >= size) {
36+
return "";
37+
}
38+
39+
// Allocate a slot for all the "saved" characters
40+
// plus one extra for the null terminator.
41+
result = calloc(size-num_spaces+1, sizeof(char));
42+
// Copy in the "saved" characters.
43+
for (i=first_non_space; i<=last_non_space; ++i) {
44+
result[i-first_non_space] = str[i];
45+
}
46+
// Place the null terminator at the end of the result string.
47+
result[i-first_non_space] = '\0';
48+
49+
return result;
50+
}
51+
52+
/*
53+
* Return true (1) if the given string is "clean", i.e., has
54+
* no spaces at the front or the back of the string.
55+
*/
56+
int is_clean(char* str) {
57+
char* cleaned;
58+
int result;
59+
60+
// We check if it's clean by calling strip and seeing if the
61+
// result is the same as the original string.
62+
cleaned = strip(str);
63+
64+
// strcmp compares two strings, returning a negative value if
65+
// the first is less than the second (in alphabetical order),
66+
// 0 if they're equal, and a positive value if the first is
67+
// greater than the second.
68+
result = strcmp(str, cleaned);
69+
70+
return result == 0;
71+
}
72+
73+
int main() {
74+
int i;
75+
int NUM_STRINGS = 7;
76+
// Makes an array of 7 string constants for testing.
77+
char* strings[] = { "Morris",
78+
" stuff",
79+
"Minnesota",
80+
"nonsense ",
81+
"USA",
82+
" ",
83+
" silliness "
84+
};
85+
86+
for (i=0; i<NUM_STRINGS; ++i) {
87+
if (is_clean(strings[i])) {
88+
printf("The string '%s' is clean.\n", strings[i]);
89+
} else {
90+
printf("The string '%s' is NOT clean.\n", strings[i]);
91+
}
92+
}
93+
94+
return 0;
95+
}

leak_report.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Leak report
2+
3+
_Use this document to describe whatever memory leaks you find in `clean_whitespace.c` and how you might fix them. You should also probably remove this explanatory text._
4+

0 commit comments

Comments
 (0)