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

C Debugging Techniques

Debugging is finding and fixing mistakes in your program, like being a detective. Printing values and adding checks show you what is going on.
Syntax
c
printf("DEBUG: variable = %d\n", variable);
printf("at %s:%d\n", __FILE__, __LINE__);

#ifdef DEBUG
    printf("verbose debug output\n");
#endif

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 standard input/output (printf, scanf)
#include <stdio.h>
// Program execution starts in main()
int main() {
	// Declare x and set it to 5
	int x = 5;
	// Print formatted text to the screen
	printf("DEBUG: x = %d\n", x);
	x = x * 2;
	// Print formatted text to the screen
	printf("%d", x);
	// Return 0 to signal that the program finished successfully
	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 standard input/output (printf, scanf)
#include <stdio.h>
// Program execution starts in main()
int main() {
	// Declare value and set it to 5
	int value = 5;
	// Print formatted text to the screen
	printf("%s:%d value=%d\n", __FILE__, __LINE__, value);
	// Return 0 to signal that the program finished successfully
	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 standard input/output (printf, scanf)
#include <stdio.h>
// Define the macro DEBUG (replaced by the preprocessor)
#define DEBUG
// Program execution starts in main()
int main() {
	#ifdef DEBUG
	// Print formatted text to the screen
	printf("Debug output enabled\n");
	#endif
	// Print formatted text to the screen
	printf("Normal output");
	// Return 0 to signal that the program finished successfully
	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 standard input/output (printf, scanf)
#include <stdio.h>
// Program execution starts in main()
int main() {
	int *ptr = NULL;
	// Check whether ptr == NULL
	if (ptr == NULL) {
		// Print formatted text to the screen
		printf("Pointer not initialized safely -- avoided dereference");
	}
	// Return 0 to signal that the program finished successfully
	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 standard input/output (printf, scanf)
#include <stdio.h>
// Program execution starts in main()
int main() {
	// Loop: repeat while the condition holds
	for (int i = 0; i < 3; i++) {
		// Print formatted text to the screen
		printf("Loop index: %d\n", i);
	}
	// Return 0 to signal that the program finished successfully
	return 0;
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Leaving debug printf calls in the finished program.
  2. Ignoring compiler warnings, which often point to real bugs.
  3. Printing pointer or array values without checking that they are valid first.

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.