Even if you are normally a precise person, you will find that the quantity of detail involved in programs means you will make lots of mistakes
Mistakes in programs are called bugs
They are completely inevitable
But on the other hand, you can get a lot of help from the computer in avoiding, finding, and fixing them
Some people take ten times longer than others to write programs, because most of their time is spent in debugging
To reach a reasonable level of productivity it is absolutely essential to reduce the percentage of time you spend debugging to reasonable levels
Initially, aim for less than 50%, but ultimately aim for a tiny percentage
The secrets of debugging are, in order of importance:
The number one secret of debugging is to go about things in the right way (see aside: agile development)
The agile rules are so important, they will be repeated here to emphasize their effects on debugging
Don't write more than a couple of lines of code before re-compiling and re-testing
That way, as soon as there is a bug, you will know exactly where it is, in the couple of lines you just wrote, and you will have a good knowledge of those lines in your head ready to find and fix the problem
Small functions allow you to make good progress
A big function takes too long to get working, but a small function can be tackled in a reasonable time
A small self-contained function is also easy to test, so you know it is working
The working functions that you have already written give you a reliable base for writing the next function
It is also a good idea to handle input/output last, in separate non-auto-tested functions
That means Don't Repeat Yourself
If you have lots of similar bits of code, even if you copy-and-paste them, you have lots of places for bugs to hide
If, instead, you can turn repeated similar snippets into a single function with an argument or two, there is now only one copy of the code, only one place for bugs to hide
Think hard about names for variables and functions
A bad name confuses you into doing the wrong thing
Don't write code that 'works by magic' - make it so that the code explains itself
Don't try to be too clever - as Kernighan once said, debugging is harder than programming, so if your program is too clever, you won't be able to debug it
Imagine you are writing the program for a lesser programmer to take over from you later
The coursework assignments show you a simple DIY way to add automated tests to your programs (you don't need a complex framework)
Automated tests provide huge levels of confidence that things are working and that you are making good progress
Otherwise, a bug arises, and you have to spend a lot of time finding out which function the bug is in
As you progress with a program, it is normal to find that what you've done already needs to be changed
For a radical change, comment out most of the functions and tests, and repair each function and its tests one by one according to the new point of view
That way, the radical change doesn't need superhuman effort, and results in a confident working result
Don't over-engineer programs
The KISS rule is a tough one, and needs quite a lot of experience, because it is about design, which will never become completely routine in your whole career
Just use each program as a learning experience, which teaches you how you keep your program designs simpler in the future
Assuming that you are going about things in the right way, following all the agile rules, the next step is to let the compiler help
Learn to interpret compiler error messages and warning messages
And never ignore them, always track down why you are getting them, and get rid of them
And switch on all the compiler warnings you can find (-Wall
as a
minimum)
There are two general rules about reading error and warning messages from the compiler
First: if the compiler spews out lots of error messages, don't panic, just read and fix the first and ignore the rest (they are probably just knock-on effects anyway)
Second: if the compiler says there is a problem in line 15, check that the problem isn't at the end of line 14, e.g. a missing semicolon (the problem can't after the place the message says, but it can be before)
If you have a stubborn bug, you may need to find it by tracing what your program is doing
The simplest way to do that is to add printf
statements
You can add printf("A\n")
, printf("B\n")
, etc. in
various places to
find out how far the program is getting before trouble strikes
Or you can add printf("n=%d\n",n)
to display the value of a
variable at a critical point in the program
Rather than just randomly sticking in print statements, you should have a strategy
Each program run should narrow down the problem, preferably to half what it was before
For example, a print statement half way through the execution of the program might tell you whether the bug happens early or late
Or a printout of some variables might tell you which of them becomes incorrect by a certain point
There is a debugging tool called gdb
which can help
Probably, you should use it rarely, as a last resort, because it can eat up a lot of your time
It is also somewhat a matter of taste whether you like using it
You must compile with the -g
option
Suppose you have a program with a bug in it that you run with
./prog file
gdb ./prog b main put a breakpoint at the start r file run the program with its args s or n one step or next line p x print out x q quit
You need to learn about GDB features one by one
Generally, the most useful commands are the ones with one-letter or two-letter abbreviations:
b breakpoint d delete breakpoint c continue h help l list s step n next (step over rather than step into) p print q quit r run bt backtrace