Outline 11: Miscellaneous C Programming Issues
Held: Thursday, 16 April 2015
Back to Outline 10 - Improved Make.
On to Outline 12 - Testing.
Summary
We consider a variety of smaller issues that might make you a better
C programmer.
Related Pages
Overview
- Sorting, revisited.
- Function pointers.
- Testing C programs.
- Testing with macros.
- Testing through the command line.
- Testing with
assert.
- Memory issues in C programs.
- Checking memory problems with valgrind.
Administrivia
- Homework: TO be determined.
- Cool CS talk today at 4:30 p.m.
- Alumni talk tonight about social media.
Sorting
- Suppose we have the following function
/**
* Sort an array of strings alphabetically.
*/
int strings_sort (int n, char *strings[])
- Now, suppose you want to sort arrays of strings by length. How would
you build the new function?
- Option 1 - Copy, paste, change
- But that should set off red flags.
- Option 2 - Factor out the common code with macros
- E.g., write a macro that generates the body of the sort routine,
but takes the comparator as a parameter
- Leads to name expansion!
- Option 3 - Pass in the comparator as a parameter
/**
* Sort an array of strings using comparator.
*/
int strings_sort (int n, char *strings[], "*something-that-compares-strings*" compare)
Function Pointers
- But how do you pass a function as a parameter in C?
- In C, we say that you pass in a
function pointer
- Basically, we write a declaration of the variable as a function, but
also add a star (it's a pointer) and parenthesize (for clarity)
int strings_sort (int n, char *strings[],
int (*compare)(char *str1, char *str2));
- You can declare variables of this type, too
int (*binary_integer_function)(int x1, int x2);
- But many people find these declarations. And so they define types
instead, (e.g.,
StringComparator)
- You declare function pointer types much like you declare function pointers.
The only real difference is that you use typedef before the
declaration. (Yeah, that's how typedef always seems to work.)
- For example,
typedef int (*StringComparator)(char *str1, char *str2);
int strings_sort (int n, char *strings[], StringComparator compare);
- There are two ways to call the function.
- You can write
(*compare)(str1, str2)
compare (str1, str2);
assert - simple program checking
- As you've seen, there are a variety of approaches to testing C programs.
- A very common approach (one that is included in every C implementation
I know) is to use program assertions.
- An assertion is essentially a statement that "I expect this condition
to hold at this point in the program".
- It's like a test, but at a finer grain.
- For example
i = 5;
// I assume i is 5
foo (i);
// I assume i is still 5, since C is a pass-by-value language
- The
assert macro takes as parameter a Boolean expression,
executes the expression, and, if it fails, terminates the program and
reports the line at which it terminated.
- You can turn off the assert macro with the -DNDEBUG flag.
- Don't call assert with operations that have side effects!
It often leads to Heisenbugs.
Memory Issues in C Programs
- One of the benefits and hazards of working with C is that you have
direct access to memory.
- There are a host of problems that people encounter when working with
memory.
- I'll make you list yours.
- Here are some of mine:
- Memory leaks: Failing to free memory that you've allocated.
- Invalid references: Continuing to use memory after it's been freed.
- Invalid array indices: Referencing outside the bounds of the array.
- Many of these errors are subtle. They don't cause problems at the time
you make the error, they cause problems elsewhere in the program.
- Tracking them down is important!
Valgrind: Tracking memory issues
- Valgrind is a swiss-army-knife of program analysis, but I use it primarily
for its memory checking features.
- You run valgrind on a program you've compiled.
- It runs the program a bit more slowly, and reports on all of the memory
issues it's encountered.
- It even reports on errors that it hasn't encountered.
- We'll take a look at a variety of programs. The errors in these programs
are all fairly explicit, but are the kinds of things that often happen
inadvertently.
- One that runs correctly.
- An out-of-bounds index accompanied by a failure to free memory.
- Inadvertent reuse of stack memory. (In two forms.)
- Inadvertent reuse of heap memory. (In two forms.)