Programming in a sense is easy. But bugs are something which always makes more trouble. Can anyone help me with good debugging tricks and softwares in c?
The following are popular debugging tools.
Some very simple Tricks/Suggestions
-> Always check that nowhere in your code you have dereferenced a wild/dangling pointer
Example 1)
int main()
{
int *p;
*p=10; //Undefined Behaviour (crash on most implementations)
}
Example 2)
int main()
{
int *p=malloc(sizeof(int));
//do something with p
free p;
printf("%d", *p); ////Undefined Behaviour (crash on most implementations)
}
-> Always initialize variables before using
int main()
{
int k;
for(int i= k;i<10;++i)
^^
Ouch
printf("%d",i");
}
Unit testing. Makes getting your software correct a lot easier.
You might also want to hone your debugging skills by reading the book Debugging by David Agans. Every programmer should read this early on in their career.
From "The Elements of Programming Style" Brian Kernighan, 2nd edition, chapter 2:
Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?
So from that; don't be "too clever"!
But apart from that and the answers already given; use a debugger! That is your starting point tool-wise. You'd be amazed how many programmers struggle along without the aid of a debugger, and they are fools to do so.
But before you even get to the debugger, get your compiler to help you as much as possible; set the warning level to high, and set warnings as errors. A static analysis tool such as lint, pclint, or QA-C would be even better.
- gdb is a debugger to analyse your program.
- Other techinque is to use printf or logs
- Valgrind provides dynamic analysis of the executable
- Purify provides static and dynamic analysis. Sparrow and Prevent are some other tools in competition to Purify.
Tools for debugging are all well and good and for some classes of error they will just point you straight to the problem. The best tip that I have for debugging is that you need to think about it in the right way. What works for me is the following:
- The compiler probably isn't broken. I've been working with C for 25 years now and in all that time it's almost invariably something I'm doing wrong.
- Read the error messages. Often I've looked back at the error message and in hindsight realized it was telling me exactly what was wrong.
- Read the documentation. Make sure you aren't making assumptions about the language or library that aren't true.
- Make a mental model of the problem. I ask myself what needs to be hapening in my code in order for the results I'm seeing to occur. Then add debug statements, assertions or just step through in the debugger (if you can) to see what is really happening.
- Talk the problem through with someone else. Just describing it to a a third party often results in a revelation about what might be happening.
Other people will have other ways of approaching debugging, but I find if you have a structured approach to it rather than flailing around changing stuff at random you usually get there and when you do be prepared for the inevitable Why didn't I see that straight away!
This can be separated into:
Prevention measures:
- Use strict coding styles, don't make a mess
- Use comments and code revisions
- Use static code analysis tools
- Use assertions where it's possible
- Don't over complicate
Post-factum
- Use debugger/tracer
- Use memory checking tools
- Use regression testing
- Use your brain
In addition to all the other suggestions (gdb, valgrind, all that), some simple rules when writing the code help a lot when debugging afterwards.
- Always use types with the proper
semantics. Unsigned types (best
size_t
) for array indices and numbers that represent a cardinal,ptrdiff_t
for pointer differences,off_t
for file offsets etc.enum
types for tags and case distinctions. - There is almost no need for the
builtin types
int
,long
,char
or whatever. Avoid them whenever possible. - In particular don't use
char
for arithmetic, the signedness problems with that are a plague. Useuint8_t
orint8_t
if you feel the need for such a thing. - Always initialize variables, all of them: integer,
double
, pointers,struct
. It is not true that this is less efficient with a modern compiler. In most cases it will just be optimized away when not necessary. But especially pointer variables that are not properly initialized can produce spurious errors and make code hard to debug. If you have them initialized toNULL
your program will fail early, and your debugger will show you the place. - Compile with all warnings on, and don't finish tidying your code until the compiler doesn't give a single warning. They are quite good at that nowadays, take advantage.
- Compile with different optimization options on, or even better with different versions of your compiler, or still better with completely different compilers on different platforms.
- Use the
assert
macro. This forces you to think of your assumptions and also make your code fail early if they are not fulfilled.
valgrind for memory problems if you're on linux. use gdb/ddd on linux as well. On windows a lot of windows programmers don't seem to be knowledgeable of windbg. It is very useful but has a learning curve like gdb; more powerful than the built in debugger in visual studio. learn to use assert, you will catch lots of stuff and you can turn it off in release code if you so choose. Use a unit testing framework like Check, cunit, etc . Always initialize your pointer, to NULL if nothing else. When you free a pointer set it to NULL. Better you to catch a segfault than your user. Pick a coding standard and stick to it, consistency will help you make fewer mistakes. Keep your functions small if at all possible, this will keep you from having 10 level deep braces which are logic nightmares. If compiling using gcc use -Wall and -Wextra . Use the strn* functions instead of str* functions. Well worth the extra thinking they force you to do.