C
Update: 2022-04-26: Overhauling to be simplier and bsd/clang friendly.
Rules of engagement⌗
- How I program C by Eskil Steenberg
- Notes on Programming in C by Rob Pike
- The Ten Commandments for C Programmers by Henry Spencer
Style⌗
- suckless
- FreeBSD
man 9 style
The language⌗
Basic Types⌗
Integer Types⌗
C99 with stdbool.h
introduced boolean type _Bool
(1 byte), and macros true
(1) and false
(0).
Type | Storage (bytes) |
---|---|
_Bool |
1 |
char |
1 |
unsigned char |
1 |
signed char |
1 |
int |
2-4 |
unsigned int |
2-4 |
short |
2 |
unsigned short |
2 |
long |
4 |
unsigned long |
4 |
long long(*) |
8 |
unsigned long long(*) |
8 |
Interestingly the specific storage size for int
is not defined. A compilers given implementation bit widths are defined in limits.h
.
Real and Complex Floating Types⌗
Most compilers honour the IEEE 754-1985 standard.
Type | Storage (bytes) | Precision (decimal) |
---|---|---|
float |
4 | 6 |
double |
8 | 15 |
long double |
10 | 19 |
void⌗
Represents no value, and is widely used in three scenarios:
- Functions that do not return a value, e.g.
void exit (int status)
, or where the return value is to be explicitly discarded e.g.(void)printf("mt tennent");
- Prototypes of functions that have no parameters, e.g.
int rand(void)
- Pointers to void i.e.
void *
(typeless pointers)
Memory Access Operators⌗
The address of operator, &
, yields the address of its subject, thus &foo
is a pointer to foo
.
The indirection operator, *
, is used to access an object or function through a pointer.
{% highlight c %} int32_t num, *pnum; pnum = # *pnum = 123; {% endhighlight %}
The subscript operator, []
, can be used to address elements within an array. In reality this is sugar for offsetting into chunk of memory, or in other words x[i]
is equivalent to (*(x+(i)))
{% highlight c %} float arr[10] = { 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7 }, *parr; parr = arr; parr = parr + 3; {% endhighlight %}
Member of a struct or union operators, .
, and its pointer friendly version ->
. The arrow operator ->
is a convenient pointer dereference. That is, p->x
is equivalent to (*p).x
{% highlight c %} struct { wchar_t *name; float amount; } label, *plabel; label.amount = 49.95; plabel = &label; plabel->name = “Bill Joy”; {% endhighlight %}
Strings⌗
The Pre Processor⌗
Macros⌗
This following macro is called variadic. When the macro is invoked, all the tokens in its argument list after the last named argument, including any commas, become the variable argument. This sequence of tokens replaces the identifier __VA_ARGS__
in the macro body wherever it appears.
#define stopif(assertion, action, ...) {
if (assertion) {
fprintf(error_log ? error_log : stderr, **VA_ARGS**);
fprintf(error_log ? error_log : stderr, "\n");
action;
}
}
//usage
stopif(!isnan(inv_total), goto nanval, "Invoice amount was NaN");
stopif(!foo, return -1, "wtf dude, foo must not be NULL");
nanval:
free(fooheap);
return NAN;
Another one. The macro print_log
acts as the public API, encapsulating internal grunt function _print_log
:
#define print_log(level, ...) do {
time_t \_clk = time(NULL);
\_print_log(level, **FILE**, **func**, ctime(&\_clk), **LINE**, **VA_ARGS**);
} while (0)
extern void \_print*log(int, char *, const char \_, char*, int, char *, ...);
//usage
print_log(LOG_WARNING, "where be a wookie when you need one");
Patterns⌗
vtables⌗
Virtual tables are a technique for adding functions outside of an objects struct. Using a hash table as the vtable data structure, the key, something unique to the object and the specific function (in case you want to store multiple functions in the vtable for the same object), and the value, the actual function.
person_s.h⌗
typedef struct person_s {
char *name;
char **books;
int len;
void (*print)(struct person_s*);
};
The object, a person. The print
function pointer here is souly used as the hash key. It’s unique to both the object, and the specific operation of printing. It never points to an actual function.
print_vtable.h⌗
extern HashTable *funcs_hash;
typedef void (_print_fn_type)(struct person_s_);
void check_print_fn(print_fn_type pf);
##define printfn_vtable_add(object, fn) { \
check_print_fn(fn); \
hash_table_insert(funcs_hash, (object)->print, fn); \
}
void print_person_frontend(struct person_s *in);
Declares hash table, a macro to add functions to the hash table (this exists only to add static type checking on the function passed, by first trying to pass it into check_print_fn
), and the “frontend” function signature (for consumers of the API) that takes in a person and will query the vtable for the function to invoke.
print_vtable.c⌗
HashTable *funcs_hash;
void check_print_fn(print_fn_type pf) { }
void print_person_frontend(struct person_s *in) {
if (!funcs_hash) {
funcs_hash = hash_table_new(g_direct_hash, g_direct_equal);
}
print_fn_type printfn = hash_table_lookup(funcs_hash, in->print);
if (printfn) {
printfn(in);
return;
}
printf("No print implementation found in vtable...panic!\n");
}
main.c⌗
static void print_person_backend(struct person_s *in) {
printf("\nName: %s\n", in->name);
for (int i=0; i < in->len; i++)
printf("- Book author for: %s\n", in->books[i]);
}
int main() {
struct person_s dennis = {
.name="Dennis Ritchie",
.books = (char\*[]) { "The C Programming Language - 1978" },
.len = 1
};
print_person_frontend(&dennis);
printfn_vtable_add(&dennis, print_person_backend);
print_person_frontend(&dennis);
struct person_s rob = {
.name = "Rob Pike",
.books = (char*[]) { "The Unix Programming Environment - 1982", "The Practice of Programming - 1999" },
.len = 2
};
print_person_frontend(&rob);
return 0;
}
Output:
No print implementation found in vtable...panic!
Name: Dennis Ritchie
- Book author for: The C Programming Language - 1978
Name: Rob Pike
- Book author for: The Unix Programming Environment - 1982
- Book author for: The Practice of Programming - 1999
Libraries⌗
The C standard library and the C POSIX library both offer a standardised, resuable slabs of macros, type definitions and functions for tasks such as string handling, mathematical computations, input/output processing, memory management, and several common operating system services. A set of common building blocks.
POSIX being a superset of the C standard library. If C and POSIX is ever in conflict, C wins.
C standard library⌗
Header | Description |
---|---|
<assert.h> |
Contains the assert macro, used to assist with detecting logical errors and other types of bug in debugging versions of a program. |
<complex.h> |
A set of functions for manipulating complex numbers. |
<ctype.h> |
Defines set of functions used to classify characters by their types or to convert between upper and lower case |
<errno.h> |
For testing error codes reported by library functions. |
<fenv.h> |
Defines a set of functions for controlling floating-point environment. |
<float.h> |
Defines macro constants specifying the implementation-specific properties of the floating-point library. |
<inttypes.h> |
Defines exact width integer types. |
<iso646.h> |
Defines several macros that implement alternative ways to express several standard tokens. For programming in ISO 646 variant character sets. |
<limits.h> |
Defines macro constants specifying the implementation-specific properties of the integer types. |
<locale.h> |
Defines localization functions. |
<math.h> |
Defines common mathematical functions. |
<setjmp.h> |
Declares the macros setjmp and longjmp, which are used for non-local exits. |
<signal.h> |
Defines signal handling functions. |
<stdalign.h> |
For querying and specifying the alignment of objects. |
<stdarg.h> |
For accessing a varying number of arguments passed to functions. |
<stdatomic.h> |
For atomic operations on data shared between threads. |
<stdbool.h> |
Defines a boolean data type. |
<stddef.h> |
Defines several useful types and macros. |
<stdint.h> |
Defines exact width integer types. |
<stdio.h> |
Defines core input and output functions |
<stdlib.h> |
Defines numeric conversion functions, pseudo-random numbers generation functions, memory allocation, process control functions |
<stdnoreturn.h> |
For specifying non-returning functions. |
<string.h> |
Defines string handling functions. |
<tgmath.h> |
Defines type-generic mathematical functions. |
<threads.h> |
Defines functions for managing multiple Threads as well as mutexes and condition variables. |
<time.h> |
Defines date and time handling functions |
<uchar.h> |
Types and functions for manipulating Unicode characters. |
<wchar.h> |
Defines wide string handling functions. |
<wctype.h> |
Defines set of functions used to classify wide characters by their types or to convert between upper and lower case. |
C POSIX library⌗
Header | Description |
---|---|
<aio.h> |
Asynchronous input and output |
<arpa/inet.h> |
Functions for manipulating numeric IP addresses (part of Berkeley sockets) |
<assert.h> |
Verify assumptions |
<complex.h> |
Complex Arithmetic, see C mathematical functions |
<cpio.h> |
Magic numbers for the cpio archive format |
<ctype.h> |
Character types |
<dirent.h> |
Allows the opening and listing of directories |
<dlfcn.h> |
Dynamic linking |
<errno.h> |
Retrieving Error Number |
<fcntl.h> |
File opening, locking and other operations |
<fenv.h> |
Floating-Point Environment (FPE), see C mathematical functions |
<float.h> |
Floating-point types, see C data types |
<fmtmsg.h> |
Message display structures |
<fnmatch.h> |
Filename matching |
<ftw.h> |
File tree traversal |
<glob.h> |
Pathname “globbing” (pattern-matching) |
<grp.h> |
User group information and control |
<iconv.h> |
Codeset conversion facility |
<inttypes.h> |
Fixed sized integer types, see C data types |
<iso646.h> |
Alternative spellings, see C alternative tokens |
<langinfo.h> |
Language information constants – builds on C localization functions |
<libgen.h> |
Pathname manipulation |
<limits.h> |
Implementation-defined constants, see C data types |
<locale.h> |
Category macros, see C localization functions |
<math.h> |
Mathematical declarations, see C mathematical functions |
<monetary.h> |
String formatting of monetary units |
<mqueue.h> |
Message queue |
<ndbm.h> |
NDBM database operations |
<net/if.h> |
Listing of local network interfaces |
<netdb.h> |
Translating protocol and host names into numeric addresses (part of Berkeley sockets) |
<netinet/in.h> |
Defines Internet protocol and address family (part of Berkeley sockets) |
<netinet/tcp.h> |
Additional TCP control options (part of Berkeley sockets) |
<nl_types.h> |
Localization message catalog functions |
<poll.h> |
Asynchronous file descriptor multiplexing |
<pthread.h> |
Defines an API for creating and manipulating POSIX threads |
<pwd.h> |
passwd (user information) access and control |
<regex.h> |
Regular expression matching |
<sched.h> |
Execution scheduling |
<search.h> |
Search tables |
<semaphore.h> |
POSIX semaphores |
<setjmp.h> |
Stack environment declarations |
<signal.h> |
Signals, see C signal handling |
<spawn.h> |
Process spawning |
<stdarg.h> |
Handle Variable Argument List |
<stdbool.h> |
Boolean type and values, see C data types |
<stddef.h> |
Standard type definitions, see C data types |
<stdint.h> |
Integer types, see C data types |
<stdio.h> |
Standard buffered input/output, see C file input/output |
<stdlib.h> |
Standard library definitions, see C standard library |
<string.h> |
Several String Operations, see C string handling |
<strings.h> |
Case-insensitive string comparisons |
<stropts.h> |
Stream manipulation, including ioctl |
<sys/ipc.h> |
Inter-process communication (IPC) |
<sys/mman.h> |
Memory management, including POSIX shared memory and memory mapped files |
<sys/msg.h> |
POSIX message queues |
<sys/resource.h> |
Resource usage, priorities, and limiting |
<sys/select.h> |
Synchronous I/O multiplexing |
<sys/sem.h> |
XSI (SysV style) semaphores |
<sys/shm.h> |
XSI (SysV style) shared memory |
<sys/socket.h> |
Main Berkley sockets header |
<sys/stat.h> |
File information (stat et al.) |
<sys/statvfs.h> |
File System information |
<sys/time.h> |
Time and date functions and structures |
<sys/times.h> |
File access and modification times |
<sys/types.h> |
Various data types used elsewhere |
<sys/uio.h> |
Vectored I/O operations |
<sys/un.h> |
Unix domain sockets |
<sys/utsname.h> |
Operating system information, including uname |
<sys/wait.h> |
Status of terminated child processes (see wait) |
<syslog.h> |
System error logging |
<tar.h> |
Magic numbers for the tar archive format |
<termios.h> |
Allows terminal I/O interfaces |
<tgmath.h> |
Type-Generic Macros, see C mathematical functions |
<time.h> |
Type-Generic Macros, see C date and time functions |
<trace.h> |
Tracing of runtime behavior (DEPRECATED) |
<ulimit.h> |
Resource limiting (DEPRECATED in favor of <sys/resource.h>) |
<unistd.h> |
Various essential POSIX functions and constants |
<utime.h> |
inode access and modification times |
<utmpx.h> |
User accounting database functions |
<wchar.h> |
Wide-Character Handling, see C string handling |
<wctype.h> |
Wide-Character Classification and Mapping Utilities, see C character classification |
<wordexp.h> |
Word-expansion like the shell would perform |
Unit Testing⌗
- Unity Project - Unity is most obviously about assertions. Assertions are statements of what we expect to be true about our embedded system.
- GLib Testing