Languages like Java and C++ were developed out of C to support (i.a.) better encapsulation and information hiding. C only provides very basic support, and otherwise expects enough discipline from the programmer to avoid breaking any intended encapsulation.
There are no classes and no packages in C, so there is nothing for C functions to belong to, and they must be carefully named to avoid clashes. C structures encapulate several data as a unit, but do not restrict access, so there is no abstraction.
The use of header files and separate modules
can provide some hiding of internals. An empty structure
type might be used in the published header, while its
full declaration would appear only in the source file
that needed it, or in an unpublished header if needed by
several. Functions and globals local to a module can be
hidden from other modules with static
.
Here's a fairly robust template for abstract types in C. First, write a header declaring (say) a type for a handle to access a very simple database:
/* file db.h */ #ifndef db_included #define db_included /* pointer to incomplete structure type */ typedef struct db_handle *db_ref; /* a constructor */ db_ref db_open(const char *addr); /* methods */ int db_get(db_ref, const char *key); /* etc */ /* a destructor */ void db_close(db_ref); #endif
Note that C does not have any notion of ‘constructor’ or ‘method’. They are just ordinary functions alike.
Now write a source file to complete the structure type, and define the functions:
/* file db.c */ /* Include the header, so we ensure that our definitions and declarations are consistent. */ #include "db.h" struct db_handle { /* . . . */ }; /* Usestatic
for internal state and private functions... */ static void normalize_key(char *to, const char *from) { /* . . . */ } db_ref db_open(const char *addr) { /* Allocate astruct db_handle
, and initialize it... */ } int db_get(db_ref db, const char *key) { /* Use info indb
to access an entry... */ } void db_close(db_ref) { /* Release memory... */ }
As a result, the internal structure of your database
handle can change over time without affecting its users,
as they can't see inside it. The functions declared with
static
are not visible
outside db.c, so they won't clash with
identically named functions in other parts of the
program. However, the compiler will not force a user of
your library to initialize a db_ref
correctly with db_open
, or to release it after use
correctly with db_close
. He
is expected to have enough self-discipline to do that
himself.