K&R

C was not my first programming language. At some point I figured that there is a lot of Open Source software written in C out there, and contributing would require to learn it.

Recently I've discovered JoeQuery's blog which has a nice learning C series (amongst other really cool stuff). It inspired me to write a post about C.

If you are new to C you'll read a lot about dangerous macros, arrays and pointers, how you should handle dynamic memory allocation etc. All theses points are very important, but here is an attempt to sum up more general concepts that you are less likely to find in most C tutorials. If anything, it's a good summary of what I would have liked to read when learning C (maybe I did but forgot — there are some things you only learn by shooting yourself in the foot).

C is *very* different from C++

C++ differ from C in a vicious way. Someday, you might cross the path of some C++ code that will really looks like C. Do not try to transpose what you learn in C to C++ (even though you see struct or enum). You'll do better if you think of them as two unrelated separate languages, because that's what they really are today. I guess the opposite is also true, watch out if you're learning C coming from C++.

/*
 * - In C++, this declare a function that take no argument.
 * - In C, this declare a function that can take an arbitrary number of
 *   arguments (in an old fashion way).
 */
void	do_something();

There are many of them

You'll read a lot about "the C programming language". Truth is there are a lot of different kind of C and even more C environments (platform). Knowing your C standard (or variation) and your environment is a real plus when searching for accurate documentation and help. To get which standards are known to your compiler, check its manual about the -std= option. If you feel lost the Wikipedia page about C is a good start. About your environment it's your OS (and when it's something else it will be obvious).

Some good old K&R C brought to you by Perl v1.0.1.3 (!)

main(argc,argv,env)
register int argc;
register char **argv;
register char **env;
{
    register STR *str;
    register char *s;
    char *index();

    /* ... */

On a non-technical level, C has also many different communities with different cultures. Keep it in mind while contributing to other projects; it is always welcome to check the habits, guidelines and preferred style beforehand.

C has no namespaces

There is no built-in support of namespaces in C, but instead a long tradition of prefixing. It is important to note that prefixing with _ or __ is reserved for the implementation of the system (the compiler and C environment) . You'll read (maybe use) stuff belonging in these "reserved namespaces" and that's OK, but avoid them for your own variables, macro, and functions.

Try clang for development

clang is, like GCC, a C compiler (though both can compile other languages too). When clang started to become a serious alternative to GCC, Expressive Diagnostics was a very attractive feature. Back then understanding GCC diagnostics was a very painful remember-by-heart-what-kind-of-error-that-cryptic-message-could-report process. For example, error: expected '=', ',', ';', 'asm' or '__attribute__' before 'foo' was usually related to an unknown type (you could have mistyped int or forgot to include a header). While GCC has since improved, I still do prefer clang's default warning flags and diagnostics. This is really about personal taste though, so my advice is to play a bit with both GCC and clang to make your own mind.

A very simple and common example

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int
main(int argc, char *argv[])
{

	printf("Hello World!\n")
	return (0);
}
% gcc hello.c
hello.c: In function 'main':
hello c:8:2: error: expected ';' before 'return'
  return (0);
  ^
% clang hello.c
hello.c:7:26: error: expected ';' after expression
        printf("Hello World!\n")
                                ^
                                ;
1 error generated.

UB

C is notorious for "Undefined behavior". For example, if you divide by zero most "high level language" will throw an exception or the program will abort somehow. In C you'll get an UB, meaning it's impossible to know what will happen.

UB are tricky. If you are unlucky lucky, the program will continue to run with a potentially corrupted memory / stack, and debugging will be like wandering through a dungeon without a light torch where it's pitch black and you're likely to be eaten by a grue.

As it has been correctly pointed out to me, having UB allows compiler a whole class of optimisations. The LLVM blog posts series What Every C Programmer Should Know About Undefined Behavior explains it in details with simple examples. Thanks to grim7reaper for both the info and LLVM blog's reference!

Rigorously checking pointers and returned values for errors along with using tools like gdb, Valgrind and scan-build will save you a lot of debugging time.

premature optimization is the root of all evil

Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a em negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.

Donald Knuth

This is especially true with low level languages like C where you get to manage memory yourself. Keep it simple and stupid because most of the time n is small.

That's it!

Feel free to add your advices and tips about C in the comments section :)