C and C++ give you raw control over memory — which is exactly why they crash in more interesting ways than most languages. This guide walks through how to debug C and C++ code entirely in the browser using CoderFile's online C editor and online C++ editor, without installing gcc, g++, gdb, or anything else.
What you'll learn
- How to read compiler errors and warnings as your first debugger
- Disciplined printf / cerr logging that survives a crash
- Why segfaults happen and how to find the offending pointer fast
- Spotting undefined behavior before it bites in production
- When to stop using an online debugger and reach for gdb / valgrind
The compiler is your first debugger
Before you log a single value, turn on warnings. Most C and C++ bugs — type mismatches, uninitialized variables, dead branches, signed/unsigned comparisons — are visible to the compiler if you let it speak up.
# Online editors usually allow custom compile flags gcc -Wall -Wextra -Wpedantic -O0 -g main.c g++ -Wall -Wextra -Wpedantic -std=c++20 -O0 -g main.cpp
Treat every warning as an error you haven't found yet. A "comparison between signed and unsigned integer" warning is often the exact reason your loop runs 4 billion iterations instead of zero.
Printf debugging that actually works
The single biggest mistake developers make when debugging C online is printing to stdout right before a crash. stdout is line-buffered; if the program dies before the next newline, your log never appears. Two reliable patterns:
#include <stdio.h> // Pattern 1: write to stderr — unbuffered for printf-family
fprintf(stderr, "[DEBUG] i=%d, ptr=%p\n", i, (void*)ptr); // Pattern 2: flush stdout explicitly
printf("about to call dangerous()\n");
fflush(stdout);
dangerous();And the C++ equivalent:
#include <iostream> // std::cerr is unbuffered std::cerr << "[DEBUG] size=" << v.size() << " back=" << v.back() << '\n'; // Or flush std::cout with std::flush / std::endl std::cout << "checkpoint" << std::endl;
Include enough context that the log is useful on its own: function name, loop index, the pointer or size you actually care about. "got here" tells you nothing when there are twelve of them.
Decoding "Segmentation fault (core dumped)"
A segfault means your program touched memory the OS won't let it touch. In practice that means one of four things:
- Null pointer dereference — a pointer was never assigned, or a function returned NULL and you didn't check.
- Use-after-free — you called
free()ordeleteand then read or wrote the pointer anyway. - Buffer overrun — you wrote past the end of an array or std::vector via raw indexing.
- Stack overflow — recursion with no base case, or a huge local array like
int big[10000000].
Reproduction beats theorising. Halve your input, log the index right before the crash, then halve it again. Three iterations of binary search usually pin the line.
A worked example
#include <stdio.h>
#include <string.h> int main(void) { char *s = NULL; // forgot to allocate strcpy(s, "hello"); // boom: segfault printf("%s\n", s); return 0;
}Running this in the online C debugger prints nothing and exits with signal 11. Add one defensive log to stderr above the offending call and the cause is immediately visible:
fprintf(stderr, "s = %p before strcpy\n", (void*)s); strcpy(s, "hello");
s = (nil) in stderr → fix with s = malloc(16); and a null check.
Spotting undefined behavior
Undefined behavior (UB) is the most dangerous category of C/C++ bug because it often "works" until it doesn't. Reading uninitialized memory, signed integer overflow, and violating strict aliasing all fall here. A free smell test:
Run the same input twice. If the output changes — different values, different ordering, or a crash only sometimes — you almost certainly have UB. Compile with -fsanitize=undefined locally to confirm.
Common C and C++ traps
Returning a local address
char* f() { char buf[32]; return buf; } — buf dies at return. Return a heap pointer or take an output buffer parameter.
Off-by-one in arrays
Loop to i < n, never i <= n. Indexing arr[n] is undefined.
Iterator invalidation
Inserting into a std::vector during iteration invalidates iterators. Use indices or rebuild outside the loop.
Mixing new/delete and malloc/free
They use different allocators. Free what you new'd with delete, what you malloc'd with free. No crossing the streams.
An online C/C++ debugging workflow
- Open the editor and paste the smallest snippet that reproduces the bug.
- Compile with
-Wall -Wextra -Wpedantic. Read every warning. - Add a stderr log on the line above the crash. Re-run. The log either appears (you know where it died) or doesn't (the crash is earlier).
- Shrink the input. A crash with 10 elements is much easier than the same crash with 10,000.
- Once you can describe the bug in one sentence, fix it. Re-run with the original input to confirm.
When the browser isn't enough
Online debugging is excellent for snippets, contests, interview prep, and reproducing third-party bug reports. Switch to a local toolchain when you need:
- Interactive stepping with gdb or lldb
- Memory leak detection with valgrind or AddressSanitizer
- Multi-file builds with custom CMake or makefiles
- Long-running processes or networked services
- Profiling with perf or Instruments
For everything else, an online C/C++ debugger gets you to the answer faster because there's no setup standing between you and the bug.
Try it now
Paste your snippet into CoderFile's online C or C++ editor — warnings, stderr, exit codes, and crash signals all stream back instantly.