I became a professional programmer in the era of object oriented code, and have years of experience programming in C++. I often work on large projects that have years of legacy code in a mix of c++ and c. I feel less comfortable working on pure c parts of systems. From programming in C++ I understand all the c syntax, but there's a hole in my knowledge about how to organise a complex c program without objects, and what constitutes best practise for managing memory that I would like to fill. I learnt c++ after working as a java programmer, and think a bit more c would make me a better c++ programmer, and a bit less of a java translated into c++ programmer
This article covers many of the major differences (read both sections):
Looking into building programs with extensive use of pointers as well as low-level structs would be a good start in my opinion anyway. It also depends on what is the program built for. Are you trying to write C code on a embedded rtos board or microcontroller system?
I was in basically the same boat as you (albeit with less experience, and I started with Python rather than Java), and what worked best for me was sitting down and reading Kernighan and Ritchie. You'll be able to skim the first half of the book since you're comfortable with the syntax, but you'll definitely walk away with a better understanding of low-level memory management.
Reading the parts of the Linux kernel source that have to do with memory management also helps but is not for the faint of heart or the easily bored.
In terms of organization, looking at the POSIX APIs, especially pthreads will give you a good idea of how to organize C code. The basic rules of good C project organization are:
- Don't expose your structures. Use opaque types only.
- Use the library and data type names as prefixes for function names.
- Provide "create" and "destroy" functions for allocation/construction and destruction/deallocation.
- Pass in the opaque type as the first parameter to functions operating on that type.
- Implement the C APIs using either C or C++ (it's up to you). Obviously, use non-opaque types there.
Expanding on another answer, one way is to just write object-oriented C. This is the way most libraries I interact with behave (for example the GNOME stack is almost entirely object-oriented C, mostly based on the gobject library to make it easier). You can think of it as C++ without some of the syntactic sugar.
You end up with an API like
/* in project NewStuff; namespace is ns */
ns_foo_t *ns_foo_new ();
void ns_foo_delete (ns_foo_t *);
int ns_foo_make_waffles (ns_foo_t *this, int no_of_guests);
int main () {
ns_foo_t *my_foo = ns_foo_new ();
ns_foo_make_waffles (my_foo, 1);
ns_foo_delete (my_foo);
}
which corresponds almost exactly to
class Foo {
public:
Foo () { /* whatever */ }
int make_waffles (int no_of_guests) {}
};
int main () {
Foo *my_foo = new Foo ();
my_foo->make_waffles (1);
delete my_foo;
}
You could also check out The C Programming Language ANSI C Edition written by Kernighan and Ritchie. Yes, it's old, but it only clocks in at approximately 200 pages and covers the entire language as well as the standard library.