To understand what's wrong with my program, I need a strong understanding of the data it manipulates. Show me the data, and I can probably track down the bug. The sequence of operations shown by step-by-step debugging is important, but never helped me as much. I have invariants in mind, and I can detect invariant violations by looking at the data, not at the operations between them.
In practice it means I use Valgrind first to weed out most undefined behaviours, then printf(). Yep, printf().
Take my VM for example. Had many bugs, many of them hard to track: off-by-one errors were not detectable by Valgrind for instance, because my VM heap was a giant std::vector<Word>. The GC was wrong, the stack management was wrong, the primitives were wrong… I made errors pretty much everywhere. What saved me was a little printf() based visualization tool. I could now see every block in my heap, and if anything went wrong there, I would detect it very quickly. This became my new Valgrind.
I have no idea how gdb would have helped me there. Didn't need it anyway.
Either is fine. Most conversations about debuggers however tend to equate the debugger with something like gdb or the IDE debugging interface. People will often say the absence of such a debugger is a deal breaker for them. It feels like they forgot about printf.
As a counter claim: In my experience, programmers that don't use debuggers, are those that have difficulties understanding things. They just throw code at it until something sticks.