If I keep on writing more code then there will be a time when it will be difficult for me to organize the code.
This is your problem: get the organisation right, and the style should flow more easily.
Don't wait to organise your code: keep your code organised as you go. Although the language doesn't do it for you, code should still be organised into modules with low coupling and high cohesion.
These modules then naturally provide a namespace. Abbreviate the module name (if it is long) and prefix function names with their module to avoid collisions.
At the level of individual identifiers, these are roughly in increasing order of subjectivity:
- pick a convention and stick with it
- eg,
function_like_this(struct TypeLikeThis variable)
is common
definitely avoid Hungarian notation (sorry JNL)
unless you're willing to use it as originally intended, which means Simonyi's apps notation rather than the terrible systems version
Why? I could write an essay about this, but I'll instead suggest you read this article by Joel Spolsky, and then hunt around some more if you're interested. There's a link to Simonyi's original paper at the bottom.
avoid pointer typedefs unless they're genuinely opaque cookie types - they only confuse things
struct Type *ok;
typedef struct Type *TypePtr;
TypePtr yuck;
What do I mean by an opaque cookie type? I mean something used inside a module (or library, or whatever) which has to be passed out to client code, but that client code can't use directly. It just passes it back to the library.
For example, a database library might expose an interface like
/* Lots of buffering, IPC and metadata magic held in here.
No, you don't get to look inside. */
struct DBContextT;
/* In fact, you only ever get a pointer, so let's give it a nice name */
typedef struct DBContexT *DBContext;
DBContext db_allocate_context(/*maybe some optional flags?*/);
void db_release_context(DBContext);
int db_connect(DBContext, const char *connect);
int db_disconnect(DBContext);
int db_execute(DBContext, const char *sql);
Now, the context is opaque to the client code, because you can't look inside. You just pass it back to the library. Something like FILE
is also opaque, and an integer file descriptor is also a cookie, but isn't opaque.
A note on design
I used the phrase low coupling and high cohesion above without explanation, and I feel a bit bad about that. You can search for it, and probably find some good results, but I'll try to address it briefly (again, I could write an essay but will try not to).
The DB library sketched above shows low coupling because it exposes a small interface to the outside world. By hiding its implementation details (partly with the opaque cookie trick), it prevents client code coming to depend on those details.
Imagine instead of the opaque cookie, we declare the context struct so its contents are visible, and that includes a socket file descriptor for a TCP connection to the database. If we subsequently change the implementation to support using a shared memory segment when the DB is running on the same machine, the client needs to be re-compiled rather than just re-linked. Even worse, the client could have started using the file descriptor, for example calling setsockopt
to change the default buffer size, and now it needs a code change as well. All these details should be hidden inside our module where practical, and this gives low coupling between modules.
The example also shows high cohesion, in that all the methods in the module are concerned with the same task (DB access). This means that only the code that needs to know about the implementation details (that is, the contents of our cookie) actually have access to them, which simplifies debugging.
You can also see that having a single concern made it easy to choose a prefix to group these functions together.
Now, saying this example is good is easy (especially since it isn't even complete), but doesn't immediately help you. The trick is to watch, as you write and extend your code, for functions that do similar things or operate on the same types (which might be candidates for their own module), and also for functions that do lots of separate things that aren't really related, and might be candidates for splitting up.
order.c
, you could name the functionsorder_add()
,order_del()
and such. There may be old systems that tell you that the name must be unique within the first 8 characters. When you switch to c++ later by accident, you'll love to writeorder::add()
andorder::del()
then.