I know some higher level languages, all web based (PHP, javascript, some python). I've finally decided to learn a lower level language, and I've decided to go with C. The problem is that all the languages I use are based heavily on OOP. Seeing as (based on the research I did) C doesn't have classes or inheritance. As a result, I ask you this: How should I organize my code in C in an organized manner like OOP, without having to switch languages or just have files with endless amounts of functions?
Well, use C++ if you still want to do some OO.
If you really want to use C only, without any OO then divide your logic over multiple files, and consider every source file to be just a single object. Thus, every file would only hold a structure and functions closely related to that single structure.
"Files with endless amounts of functions"
What you really want is files with a well defined and limited set of functions. This is called modular programming. The idea is you group functions based on functionality into a single compilation unit and define the function prototypes in a header:
Foo.h:
int GetFoo();
Foo.c:
int GetFoo()
{
...
}
Its somewhat similar to how you would group a set of methods into a single class. The main difference is that there may-or-may-not be a "this" thing you are working on at any given time. That is to say you can still do essentially "object oriented" programming in C. Your object, however, becomes a parameter to your module. Here's one approach:
Bar.h:
typedef int BAR_OBJ
BAR_OBJ MakeBar();
int GetBarSize(BAR_OBJ);
void DoFooToBar(BAR_OBJ, ...)
Bar.c
struct BarDetails
{
int isFree;
int size;
...
// other info about bar
};
static BarDetails arrayOfBars[...]; //
BAR_OBJ MakeBar()
{
// search BarDetails array, find free Bar
}
int GetBarSize(BAR_OBJ obj)
{
return arrayOfBars[obj];
}
void DoFooToBar(BAR_OBJ, ...)
{
// lookup bar obj and do something
}
There's GObject for OOP-ish C, but well-designed structured program shouldn't be any harder to read/maintain than equivalent OOP one.
C doesn't offer much in the way of code organization. There's functions and files.
Whenever you want to implement an interface with hidden implementation, or perhaps with private functions, create two files, like "foo.h" and "foo.c".
The interface and/or public functions are function prototypes in "foo.h", and the functions are written in "foo.c". In addition, any hidden implementation or other private functions are in "foo.c", but marked as static
, such as static int foo_internal_function(int i);
. Functions marked static
may only be referenced in the file they exist in, not any other.
There are no formal namespaces, although you can get much the same effect with prefixing function names, such as Foo_discombobulate_developers()
. There are no classes, although you can at least get some encapsulation effects with the files. There is no extensible hierarchy, just files and functions.
C doesn't have anywhere near the organization of later languages, but lots of really good programs have been written in it. Be careful, comment anything confusing (and in C there are likely to be confusing things), and keep good notes.
Here's a pattern that pops up in C frequently that emulates OOP:
Consider a class named MyClass.
/* MyClass.h or myclass.h */
#ifndef MYCLASS_H
#define MYCLASS_H
struct myclass_s;
typedef struct myclass_s myclass_t;
myclass_t * myclass_new();
void delete_myclass(myclass_t *);
// Method int doMyStuff(int arg1,int arg2)
int myclass_doMyStuff(myclass_t *, int arg1, int arg2);
#endif //MYCLASS_H
The header file defines the type myclass_t, but hides the actual implementation myclass_s. This somewhat ackward requirement to have two names follows from C having structs in a separate namespace, while in C++ structs are in the same namespace as all other types. The code is intended to work in both C and C++. Here is the corresponding .c file:
/* MyClass.c or myclass.c */
#include "myclass.h" // Or MyClass.h
struct myclass_s {
int exampleField;
};
myclass_t * myclass_new()
{
myclass_t * o=(myclass_t*)malloc(sizeof(myclass_t));
// Initialize o here.
return o;
}
void myclass_delete(myclass_t * o)
{
// Do any cleanup needed on o here.
free(o);
}
int myclass_doMyStuff(myclass_t * o,int arg1,int arg2)
{
// ...
}
Inheritance and dynamic binding are also possible to do in C, but they are somewhat more involved. The above pattern, or any pattern that models OOP, isn't always the best way to do things in C, so try not to get hung up on the class centered way of thinking. Still, this pattern is occasionally useful. For example, libpng uses something close to this (they also do 'exceptions' using setjmp/longjmp, something which I advice against).