← Back to C Course | Chapter 12: Advanced Topics | Lesson 17 of 20

C Debugging Techniques

Printf Debugging

Inserting printf() calls at key points in your code to print the current values of variables is the simplest and most universal debugging technique in C, requiring no special tools, though it does mean remembering to remove or comment out the extra output before shipping the final program.

Example: Printf Debugging

c
#include <stdio.h>
int main() {
	int x = 5;
	printf("DEBUG: x = %d\n", x);
	x = x * 2;
	printf("%d", x);
	return 0;
}

Using Predefined Macros

The predefined macros __FILE__ and __LINE__ expand automatically to the current source file's name and the current line number, so embedding them in a debug print (e.g. printf("%s:%d value=%d\n", __FILE__, __LINE__, value);) tells you exactly where a given message came from without typing it manually.

Example: Using Predefined Macros

c
#include <stdio.h>
int main() {
	int value = 5;
	printf("%s:%d value=%d\n", __FILE__, __LINE__, value);
	return 0;
}

Conditional Debug Blocks

Wrapping debug output inside #ifdef DEBUG ... #endif blocks lets you compile the exact same source file with verbose diagnostic printing enabled for development builds and completely stripped out for release builds, just by defining or omitting the DEBUG macro at compile time.

Example: Conditional Debug Blocks

c
#include <stdio.h>
#define DEBUG
int main() {
	#ifdef DEBUG
	printf("Debug output enabled\n");
	#endif
	printf("Normal output");
	return 0;
}

Pointer Safety Checks

An uninitialized pointer holds whatever garbage address happened to be left in memory, so dereferencing it can crash unpredictably or, worse, silently corrupt unrelated memory. Initializing every pointer to NULL immediately and checking for NULL before dereferencing turns a random crash into a predictable, debuggable one.

Example: Pointer Safety Checks

c
#include <stdio.h>
int main() {
	int *ptr = NULL;
	if (ptr == NULL) {
		printf("Pointer not initialized safely -- avoided dereference");
	}
	return 0;
}

Dry Run Loop Traces

Off-by-one and boundary errors are among the most common loop bugs in C, so printing the loop index on every iteration while debugging quickly reveals whether a loop is running one time too many, one time too few, or accessing an index outside the array's valid range.

Example: Dry Run Loop Traces

c
#include <stdio.h>
int main() {
	for (int i = 0; i < 3; i++) {
		printf("Loop index: %d\n", i);
	}
	return 0;
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.