一个程序员的辩白

17 Feb 2019

Useful C tricks

C is undoubtedly one of my favorite programming language, as I wrote C code in recent years, I’ve collected several useful C tricks.

Some of ’em taken from popular open source code, you may already know, yet I still hope they can inspire you somehow.

This is an incremental list from time to time, feel free to share with me if you got any.

#1 Unused annotation

/*
 * Space between __attribute__ and ( indicates it's not a function call
 *
 * [sic] <sys/cdefs.h> (from macOS kernel module header)
 *  __unused denotes variables and functions that may not be used, preventing
 *  the compiler from warning about it if not used.
 */
#define __unused 				__attribute__ ((unused))

/**
 * Example __unused usage
 */
void foobar(int v __unused)
{
	...
}

Yet not all C/C++ compiler support GCC-derived __attribute__ keyword, in such case, you instead define a macro to suppress compiler unused warning:

#define UNUSED(arg, ...)    (void) ((void) (arg), ##__VA_ARGS__)

/**
 * Example UNUSED usage
 */
void foobar(int v)
{
	UNUSED(v);
	...
}

#2 Compile-time array size

/**
 * Use only for array type, NOT strict pointer type
 * @a			The fixed size array
 */
#define ARRAY_SIZE(a)       (sizeof(a) / sizeof(*a))

This macro used widely in Linux kernel and barebox, there’re several known variants, listing above for simplicity.

#3 Compile-time strlen(3)

/**
 * Should only used for `char[]'  NOT `char *'
 * Assume ends with null byte('\0')
 */
#define STRLEN(s)          (ARRAY_SIZE(s) - 1)

This macro used for a char array, it acts as if it called strlen(3), note that it isn’t applicable if the char array not ends with null byte, i.e. '\0'.

Since sizeof(char) always equals to 1, this macro can be further reduce into:

/**
 * Should only used for `char[]'  NOT `char *'
 * Assume ends with null byte('\0')
 */
#define STRLEN(s)          (sizeof(s) - 1)

Still, there is a generic STRLEN to deal the case aforementioned, this macro used rarely since it’s a bit of complicated.

/**
 * Should only used for `char[]'  NOT `char *'
 */
#define STRLEN(s)          (sizeof(s) - !s[sizeof(s)-1])

#4 Explicitly annotate ignore the return value

Case happens when a function return something, yet you don’t care.

For example, you know signal(2) won’t fail in such case:

#include <stdio.h>
#include <signal.h>

int main(void)
{
	signal(SIGPIPE, SIG_IGN); /* I don't give it a shit of what signal(2) returns */
	...
	return 0;
}

Or, you want to implement a microsecond-level sleep(3) function by select(2), and you don’t care about what select(2) pumps out:

#include <sys/select.h>

void usleep(unsigned int us)
{
	struct timeval tv = {us / 1000000, us % 1000000};
	/* I don't care what select(2) returns  although it may got interrupted */
	select(0, NULL, NULL, NULL, &tv);
}

Instead of write a comment tell people you don’t care return value(In real life, don’t expected people will do this nice job for you), you can prepend (void) before function call:

#include <stdio.h>
#include <signal.h>

int main(void)
{
	(void) signal(SIGPIPE, SIG_IGN);
	...
	return 0;
}
#include <sys/select.h>

void usleep(unsigned int us)
{
	(void) select(0, NULL, NULL, NULL, (struct timeval[]) {{us / 1000000, us % 1000000}});
}

Note that its usage somewhat like UNUSED macro, yet it won’t be optimized out, since it’s a function call, in most case it’ll have side-effect, compiler won’t optimize function call out by default, unless your function marked as const or pure.

(void) ... used heavily in FreeBSD kernel, for example, FreeBSD/i386/i386/pmap.c

#5 Compile-time condition assurance

/**
 * Compile-time assurance  see: linux/arch/x86/boot/boot.h
 */
#define BUILD_BUG_ON(cond)      ((void) sizeof(char[1 - 2 * !!(cond)]))

This macro originally taken from Linux source code, yet I optimized it slightly because it’s clumsy.

It mainly used for assuring some conditions, for example:

/* Assure `long' type is 64-bit long */
BUILD_BUG_ON(sizeof(long) != 8);

Any use of BUILD_BUG_ON macro will eventually be optimized out by the compiler, thus you don’t need to worry about nop execution.

#6 Branch predictions(GCC extension)

/**
 * Branch predictions
 * see: linux/include/linux/compiler.h
 */
#define likely(x)       __builtin_expect(!(x), 0)
#define unlikely(x)     __builtin_expect(!(x), 1)

Used heavily in Linux kernel, macro above is slightly different from original form.

[sic] They are hint to the compiler to emit instructions that will cause branch prediction to favour the “likely/unlikely” side of a jump instruction.

Compiler will optimize the code according to branch prediction, for examplem, if you have something like this:

char *istrdup(const char *s)
{
	size_t sz = strlen(s) + 1;
	char *p = (char *) malloc(sz);
	if (unlikely(p == NULL)) goto out_exit;
	(void) strcpy(p, s);
out_exit:
	return p;
}

The compiler may happen to optimize your code into the following form, which its code path is more likely be a pipeline instead of a jumpline:

char *istrdup(const char *s)
{
	size_t sz = strlen(s) + 1;
	register char *p = (char *) malloc(sz);
	if (likely(p != NULL)) {
		(void) strcpy(p, s);
	}
	return p;
}

Note that inverted prediction will cause CPU pipeline flush, it’s better not to use branch prediction instead of using wrong one to cause performance degradation, especially in hot code path.

#7 Side-effect-safe MIN, MAX, ABS macro(GCC extension)

#define MIN(a, b) ({        \
    __typeof(a) _a = (a);   \
    __typeof(b) _b = (b);   \
    _a < _b ? _a : _b;      \
})

#define MAX(a, b) ({        \
    __typeof(a) _a = (a);   \
    __typeof(b) _b = (b);   \
    _a > _b ? _a : _b;      \
})

#define ABS(x) ({           \
    __typeof(x) _ = (x);    \
    _ > 0 ? _ : -_;         \
})

Those three are common used math macros, which they’re all type-agnostic, and eliminated possible side-effects, it uses GCC typeof keyword.

#8 Sizeof a struct’s member

#define STRUCT_MEMBER_SIZEOF(s, m)		sizeof((*((s[0]){})).m)

Used to get sizeof result inside a struct, without intervention of a struct variable, i.e. struct_var.some_member.

For example, if you want to bind(2) a socket with a name(path), you should check if given path length exceeds struct sockaddr_un.sun_path:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/un.h>

#define STRUCT_MEMBER_SIZEOF(s, m)		sizeof((*((s[0]){})).m)

/**
 * Create a named socket(trivial implementation)
 * @path        The socket name [XSI]
 * @return      file descriptor returned if success
 *              -1 if failed(errno will be set accordingly)
 */
int create_named_socket(const char *path)
{
    /* XXX: Use of STRUCT_MEMBER_SIZEOF macro */
    static size_t LIMIT = STRUCT_MEMBER_SIZEOF(struct sockaddr_un, sun_path);
    int fd = -1;
    size_t len;
    struct sockaddr_un sun;

    assert(path != NULL);

    len = strlen(path);
    /* struct sockaddr_un.sun_path[10x] */
    if (len >= LIMIT) {
        errno = ENAMETOOLONG;
        fprintf(stderr, "Path %s too long  %zu vs %zu\n", path, len, LIMIT);
        goto out_exit;
    }

    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0) {
        fprintf(stderr, "socket(2) fail  errno: %d\n", errno);
        goto out_exit;
    }

    sun.sun_family = AF_UNIX;
    (void) strcpy(sun.sun_path, path);
#if defined(__APPLE__) || defined(BSD)
    sun.sun_len = (unsigned char) SUN_LEN(&sun);
#endif

    if (bind(fd, (struct sockaddr *) &sun, sizeof(sun)) < 0) {
        fprintf(stderr, "bind(2) fail  fd: %d path: %s errno: %d\n", fd, path, errno);
        goto out_fail;
    }

    if (listen(fd, SOMAXCONN) < 0) {
        fprintf(stderr, "listen(2) fail  fd: %d path: %s errno: %d\n", fd, path, errno);
        goto out_fail;
    }

    printf("socket created   fd: %d path: %s\n", fd, path);

out_exit:
    return fd;
out_fail:
    (void) close(fd);
    fd = -1;
    goto out_exit;
}

Wrap up

leiless/assertf.h is a C assertion library(header-only), which enhanced compile-time/runtime assertions, it also included some frequently used macros aforementioned.

leiless/dbg.h is a runtime code path tracing library for C89, a cheap replica of sharkdp/dbg-macro.

References

10 C99 tricks

GCC is wonderful: a better ARRAY_SIZE macro

MagicMacros - Linux Kernel Newbies

__attribute__((const)) vs __attribute__((pure)) in GNU C

C Programming Tips and Tricks for Beginners – Top 15

What is your favorite C programming trick? [closed]