If possible, it's recommended that you test the more complex routines separately, i.e. with a suitable driver - a simple main program that produces the correct input for the routine, and helps you analyze the resulting output. The extra work involved in preparing the driver is worth it, debugging is much easier when you don't have to take into consideration complex inter-routine interactions. Whenever possible, routines should be tested individually for conformance to their specification. Prepare a test suite that probes the working of the various interesting combinations of inputs, trying to exercise all possible execution paths within the routine. A good way to test a complete program is to have a 'verbose mode' in your program, a 'mode' that writes to a FILE all key results.
Some programmers develop a 'subsystem classification'.
See the chapter on preprocessors. The first rule about debugging is staying cool, treat the misbehaving program as an intellectual exercise, ignore schedules and bosses. If you are not sure what causes the bug, DON'T try semi-random code modifications, that's seldom works. If you are familiar with a debugger use it, but be careful not to be carried away by the many options and start playing.