AboutBlogContact
DevOps & InfrastructureDecember 1, 1998 2 min read 125Updated: June 22, 2026

C Preprocessor Wizardry: Debugging and Logging (1998)

AunimedaAunimeda
📋 Table of Contents

C Preprocessor Wizardry: Debugging and Logging

Every time I see a developer manually adding and removing printf("here\n"); statements, I cringe. We have a compiler; let's make it work for us. By using some clever macro tricks, we can create a logging system that is invisible in production builds but incredibly detailed during development.

The Basic DEBUG Macro

The most important trick is the variable-argument macro (though in '98, we're often limited by older compilers, so we use a specific style).

#ifdef DEBUG
    #define LOG(msg) fprintf(stderr, "DEBUG: %s at %s:%d\n", msg, __FILE__, __LINE__)
#else
    #define LOG(msg)
#endif

The __FILE__ and __LINE__ macros are built-in and tell you exactly where the error occurred. No more guessing which "here" was printed.

Handling Arguments

If you need to print formatted strings, you can use the "double-parentheses" trick to handle multiple arguments in older C89 compilers:

#ifdef DEBUG
    #define DBG_PRINT(args) printf args
#else
    #define DBG_PRINT(args)
#endif

// Usage:
DBG_PRINT(("Value of x: %d\n", x));

Notice the extra parentheses! The preprocessor sees it as a single argument to the macro, which then expands into a valid printf call.

Compile-Time Assertions

Why wait for a crash? Use macros to catch errors at compile time.

#define CASSERT(predicate, name) \
    typedef char assertion_failed_##name[(predicate) ? 1 : -1]

// Usage: ensure integers are 32-bit
CASSERT(sizeof(int) == 4, int_is_not_32_bit);

If the predicate is false, the compiler will try to declare an array with size -1, which is a hard error. This costs exactly zero bytes in your final binary but saves hours of debugging on weird architectures.

Real programmers don't use debuggers; they write code that debugs itself.


Aunimeda provides DevOps engineering and infrastructure services - CI/CD pipelines, containerization, cloud deployments, and monitoring setups.

Contact us to discuss your infrastructure needs. See also: DevOps Services, Custom Software Development

Read Also

Memcached: Slab Allocation Internals (2007)aunimeda
DevOps & Infrastructure

Memcached: Slab Allocation Internals (2007)

Why is your cache server swapping? It's probably memory fragmentation. Let's look at how Memcached solves this with slabs.

Hunting Memory Leaks: Glibc Malloc Tuning (2001)aunimeda
DevOps & Infrastructure

Hunting Memory Leaks: Glibc Malloc Tuning (2001)

Is your long-running C daemon slowly consuming the entire server's RAM? Learn how to use MALLOC_CHECK_ and mallopt to keep glibc under control.

The Multi-CPU Era: Real-World Multithreading with POSIX pthreads (1999)aunimeda
DevOps & Infrastructure

The Multi-CPU Era: Real-World Multithreading with POSIX pthreads (1999)

Don't leave that second CPU idling. If you're building a 1999-era backend for a high-traffic site, you need to master POSIX threads and avoid the common pitfalls of shared state.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

DevOps Services

Get Consultation All articles