Debuggers are cool and often necessary, but I disagree with this often-expressed sentiment that print-debugging is a primitive hack for people who don't know any better. Debugging is determining the point at which the program's expected behavior diverges from its actual behavior. You often don't know where where/when this is happening. Print-debugging can give you a transcript of the program's execution, which you ca…
One neat trick that I really like for ptrintf debugging is using conditional breakpoints and putting the call to the printing function inside the breakpoint condition. This lets you add print statements without editing the original code and makes it very easy to toggle them on and off.
When I read about this more, it sounds like you can make watchpoints conditional too. You could use this pattern to print a transcript of every change that happens to a data value! Though I'm not sure if the watchpoint condition has easy access to the source file/line, so it might not be easy to log what code was changing the value.