C for C++ Programmers

As a C++ programmer you probably have heard a bit about this language
sweeping the game programming world by storm lately. With the emerging
popularity of data oriented design has come a desire to use a language
that is focused on the data. It’s called C and, thanks to some
brilliant language design decisions, it is quite similar to C++.

The C programming language came on scene anywhere from -14 to -10
years after C++ was developed by Bjarne Stoustrup. It takes all the
features of C++ that you’ve heard of, and many you haven’t!, and
tosses them out the window. Let’s start going over the basics.

Supported Environments

This may come as a surprise to you but C enjoys incredible support. In
some circles it is even considered more popular than C++! Many C++
compiler vendors are capitalizing on the similarity between the two
languages by including C language support in their compilers, so
likely you don’t even need to worry about finding a C compiler for
your target platform!

Versions of the C Language

C++ comes in several versions, such as C++98, C++03, and C++0hcomeon,
each revision bringing more and more features than the last. C,
likewise, has two major standardized versions, C89/C90, and C99. C89
and C90 are essentially the same with C90 referring to the year the
language was standardized based on the features set in C89. C99
modernizes C89 with features like declare-where-used, inline
functions, restrict qualifications for pointers, variadic macros, and
a number of other less important features like complex numbers, bools,
and standardized specified-width types. For the most part I’ll stick
with C89 because it’s the most common.

Types

C features many of the same basic types that C++ has, save one: there
is no bool. It does, however, have the myriad chars, shorts, ints, and
longs in both signed and unsigned flavours. C also features floating
point types, both single precision and double precision. And, like
C++, C also features pointers to both data and functions, but it does
not have references.

In terms of user defined types, C follows the example set by C++ and
features enumeration types as well as aggregates like unions and
structures. Enumerations are always of type int in C unlike C++ where
their backing type can be of variable size.

Unlike C++, C’s structures cannot contain constructors, destructors,
methods, or operators, they can only contain data. Function pointers
are allowed in C structures but they do not have any implicit “this”
pointer.

Syntax

User defined types like enums, structs, or unions, exist within their
own namespace. Take, for example:

struct foo {
    int member;
};

When creating instances of foo, or passing references to it around to
other functions, it must always be referenced as struct foo unless
explicitly typedef’d to something shorter, like:

typedef struct foo foo;

This typedef is inserted implicitly by C++ to avoid the extra verbosity.

User-defined namespaces do not exist, resolving conflicts requires
prefixing symbols and using static functions whenever possible. In
general using static functions more often has a side benefit of
reducing debug data which has a positive effect on build times.

Name mangling doesn’t exist within C so function overloading isn’t
permitted. Templates? No, sorry. Generic programming is a little more
difficult as a result but still possible — one example of which is
the standard library’s qsort function.

C89 implementations also have a restriction in that statements and
declarations cannot be mixed within the body of a function, all
declarations have to go at the top of their enclosing scope.

Casting from void pointers to other pointer types is not necessary.
This allows you to assign the result from, say, malloc directly to the
type you desire without casting.

struct foo *array = calloc(16, sizeof(struct foo));

Function prototypes are optional. It is possible in C89 to call a
function that has not yet been declared in the current translation
unit, and if you do so the compiler will assume that it has a return
value of type int. Although it’s legal most compilers will warn when a
function isn’t declared — if, when writing C code, you find that the
compiler is complaining about a function returning an int when you
know it should likely it hasn’t been declared.

Older versions of C technically do not permit BCPL-style single line
comments, but it seems that several compilers that adhere only to the
C89 spec have added support for them.

Of course because C has fewer features than C++ it also means that
there are many more keywords available to be used as identifiers —
just think of all the variables names you can use now!

Victim Impact Statement

I’ve been eschewing C++ in favour of C for my pet projects in the last
few months and about the only time I really miss using C++ is when
doing DirectX, but even in that case it’s not worse, just more
verbose.

// C++
d3ddevice->DrawIndexedPrimitive(...);

/* C */
d3ddevice->lpVtbl->DrawIndexedPrimitive(d3ddevice, ...);

I’m a somewhat recent convert to the “it’s the data, stupid!”
philosophy of development, thinking more about data flows than object
message passing, and personally I’ve found that removing all the
glitter/foil/keys/shiny objects that C++ has permits a more focused
approach to data. I’ve found that the capabilities of the language
haven’t restricted my work and instead have given it a new focus.
Because there’s no automatic code generation like what happens with
C++ and templates, aside from what may be done through preprocessor
macros, you know when your code size goes up.

Since adding code isn’t automatic and now requires work I find myself
thinking about ways to avoid writing more code in order to maximize my
programmer lazyness coefficient. If I need the equivalent of a virtual
function table I’ll have to build it myself and, after going through
all that pain, if I still think it was worth the effort then maybe it
really was needed!

I also have grown to like C-style interfaces exposed in header files
greatly over C++ style interfaces because they permit a much higher
degree of data hiding. By hiding my implementation details, including
both private functions and structure definitions, it also enables the
modification and refactoring of internal code without triggering
rebuilds across the board. Hallelujah! Although it is possible in C++
to expose an interface with an opaque pointer and a handful of loose
functions it isn’t idiomatic and is only seldom done.