Still though, I reach for print debugging first.
I used to print-debug a lot, but since I started working with a stack where I use an IntelliJ derivative - that is to say, where the debugger actually works - and I'm never ever going back. It's better by a huge margin.
You think you're here? Let's put a few print statements in there to confirm your execution path is actually what you think it is, and those values are what you expect them to be.
> You think you're here? Let's put a few print statements in there to confirm your execution path is actually what you think it is, and those values are what you expect them to be.
You could just put a breakpoint there, look at as many values as you want, and not have to re-run it if you think of another.
To me that's the key difference. If I know what I'm looking for, prints are great. If I don't know what I'm looking for, breakpoints are a must.
But in a normal environment ? or using C++ ? It's generally quicker (though much dirtier) to find simple bugs using print statements to track down assumptions ; when you're in the weeds with a complicated problem or need to track down a crash, then yes, bring out the big guns. But for normal development, I don't think it's completely necessary.
Eg: tracking the values of a std::vector isn't obvious, since there is std:begin and std::end iterators in the vector namespace and you have to manually dereference the whole range in the debugger.