| brian_jaress ( @ 2008-05-17 22:29:00 |
| Current mood: | |
| Entry tags: | c, programming, uh |
Two Semesters of C
When I was an undergraduate at UH, they made a big deal about Java being the default language of the department. Unless the class had to have some other language, we used Java.
But so far I've only had one graduate class with Java. The rest have
let me use whatever I want1, or told me to use C.
Lessons Learned
I'd used it before, but I'd never used it very heavily. Wrestling with the language for a couple of semesters made me realize a few things. I'm not trying to criticize C here. Every language has pitfalls, and this is really a list of things I wish I had been more aware of.
Valgrind
The good things you've heard about Valgrind are true. It found bugs I had no clue existed. Once I understood and fixed them, I started using Valgrind all the time. Be sure to use it responsibly2.
-Wall
Everyone says to compile with -Wall, and they're right. But they
rarely tell you the real reason you need it.
Here's a fragment of code that gcc compiles silently without
-Wall, assuming it has the supporting code it needs:
buffer *
new_buffer ()
{
buffer *buf = malloc (sizeof (buffer));
buf->capacity = 100;
buf->used = 0;
buf->bytes = malloc (buf->capacity);
}
If you compile it with -Wall, it'll say warning: control reaches end of
non-void function. That's because the function is declared as
returning a buffer *, but it has no return statement. You can fix it
by adding return buf; right before the closing curly brace.
There are languages where you don't declare a return type at all -- you
either return something or you don't, and if you forget to do it then it
won't happen. That's not surprising. But if you declare ahead of time
that you will return a buffer *, you sort of expect the compiler to
complain if you don't actually do it.
Use -Wall because it adds the checks you probably assumed you
already had. A lot of things suddenly start doing what they look like
they're supposed to do.
C Turns Almost Every Error into a Memory Error
If you ran that buggy buffer code, you would have a memory leak. You would also probably have a segfault when you tried to use the buffer.
What happens, as far as I can tell, is that if you call the function you
get the value of the last expression. In this case, buf->bytes
coerced into a buffer *. So you think you have a buffer, but you
really have 100 bytes. The capacity and number of bytes used are now
inaccessible. When you think you're accessing the capacity or the
number of bytes used, you're actually looking at two values in the 100
bytes. When you think you're looking in the 100 bytes . . . you're
probably segfaulting.
When I make an equivalent mistake in most other languages, I get a message telling me I forgot to return something or that I'm trying to check the capacity of something I don't have. In C it's a memory error.
In C, almost everything is a memory error3. I'm actually happy to see wrong output because it's not a segfault. Thank goodness we have Valgrind.
Software Engineering
Software engineering was quite helpful. Especially helpful were ADTs, encapsulation, unit tests, and assertions.
C actually comes with assertions in a header called assert.h. I
highly recommend it (to people already using C). C programs tend to be
full of assertion opportunities.
For the unit tests, I just had a test module with its own main
function and compiled two different programs (through two different
make targets) one using the real main and one using the test. I'm
not sure if that's the typical way.
Next Semester
More C? I don't know. It's sometimes hard to tell whether a class is going to have any coding at all.
But it's not a bad little language. If you're a real C programmer, any helpful pointers are welcome.
-
Usually Python. I spent a long time thinking I didn't have a favorite language, and then one day I realized I did and it was Python.↩
-
This should go without saying but apparently doesn't: Only fix bugs detected by Valgrind, or in any other way, after you have confirmed the bug and understand it. You could easily screw things up.↩
-
On the flip side, I've had almost no trouble with
mallocandfree. The problem (for me) is not manual memory management, it's the memory model.↩