algorithms and off by one errors

Fred

Newcomer
My coding knowledge ended in college more or less right before I took a course in how to deal with large computer projects with multiple people writing large amounts code.

One thing I don't understand how to solve efficiently with a debugger, is how to catch and solve the irratating 'off by one errors and related fencepost errors'. They are far and away my most common mistake (usually I get about 1 every 300 or 400 lines of code).

How do large projects deal with this, besides compartamentalizing the code as best they can.
 
Ive been out of the coding game for a long time now, but I used to leave post it notes around my monitor with reminders on certain things. Fencepost errors were one of those sticked. Always working with subsections of multidimensional arrays meant lots of for loops. So I made sure that I went over every for loop a few times to make sure that I was putting it down properly. After a while it just becomes second nature to double check your problem areas.

Where are you running into these errors? in your for/while loops, char arrays,....?
 
I get them everywhere. I mean im coding mostly mathematics, so theres a ton of nested for loops all over the place, but there can be string related C issues as well.

I have a nasty tendency to mix and match loop types, for instance for, + while do + do while. I do this because its usually the most elegant and compact way of solving the algorithm (eg minimizes conditionals).

However, that has the disadvantage of not really getting you in a mental habit of doing the exact same loop structure so it can lead to those error types.

Anyway, im just baffled by how people deal with scope. Its one thing when you patiently debug a 1000 line bit of code, but surely these sorts of things become prohibitively time expensive over the long haul, and I would have thought there would be smarter time tested tricks or meta code around those sorts of things.
 
For off-by-one errors in C/C++ or similar languages, memory debuggers like Valgrind or Insure++ are likely to be helpful, at least for cases where you step off the end of an array or list, or fail to initialize the first/last element of the array.
 
I get them everywhere. I mean im coding mostly mathematics, so theres a ton of nested for loops all over the place, but there can be string related C issues as well.

Brute force will get you some unmaintainable kludge that grows in complexity quickly with the number of lines of code. Break things up into functions wherever appropriate. Smaller, testable units with well-defined inputs and outputs is vastly superior to monolithic blocks of inpenetrable code.

Ideally when a function is the correct size you can see all of it at once without scrolling; you know what input it takes, what output it's supposed to give and you can see how the function works all at once without having to think through a long chain of events.

Throw in some debug asserts to validate input to functions(they compile to nothing in release mode). You can do an "int 3" from inline assembler in your assert macro if you want to invoke the debugger when an assertion failes.

If you're writting for anything serious, use unit tests and maintain a consistent coding standard between the group of people you're working with. A good idea might be to have someone else write the unit tests for your code and write the unit tests for other peoples code. Then you'll have a better grasp if the documentation for your function is up to scratch or not.

I have a nasty tendency to mix and match loop types, for instance for, + while do + do while. I do this because its usually the most elegant and compact way of solving the algorithm (eg minimizes conditionals).

Try to maintain internal consistency and avoid going against common sense.

E.g. "for ( size_t i = 0; i < nElements; ++i )" makes sense. The number of elements processed is nElements, starting from 0. 0 is the first index into an array. Consider what would have happened if you had chosen to start i off as i = 1 or used <= as the escape condition.

Find similar rules that make sense to you for while and do-while statements and learn to detect other implementations as a bad code smell.

Anyway, im just baffled by how people deal with scope. Its one thing when you patiently debug a 1000 line bit of code, but surely these sorts of things become prohibitively time expensive over the long haul, and I would have thought there would be smarter time tested tricks or meta code around those sorts of things.

Programs tend to spend some ridiculous amount of time in a tiny fraction of the code. Unless your function is part of that tiny percentage of functions that see some significant run-time, speed is simply not a concern. A full page of code is starting to get too long for comfort unless it's some kind of inner loop.
 
Off by one? Easy! Just add a +1 at the end of the algorithm! :D
 
Fred,
If you are coding on *nix or have access to gcc, a very useful tool "gcov" (coverage). This counts the execution of each line of code (over numerous runs if you want) and, very usefully, highlights/flags those that don't get executed at all.
 
Also, (when using gcc) try the -fmudflap options if your version supports them. But generally someone coding more often than once-in-a-while should really not be making these sorts of mistakes regularly.

So if you get back in your coding chair, I'm sure it'll improve quickly. :)
 
[maven];1114560 said:
Also, (when using gcc) try the -fmudflap options if your version supports them. But generally someone coding more often than once-in-a-while should really not be making these sorts of mistakes regularly.
Interesting. I'd never heard of "fmudflap".
(ahh... I see... I'm still using an ancient gcc)

I do, however, frequently use "electric fence" to catch pointer problems.

Fred,
You might also try putting in "assert(condition)" statements - this can really help the debugging process.
 
Fred,
You might also try putting in "assert(condition)" statements - this can really help the debugging process.
Beware the disappearing assert() problem though. The 'condition' statement is only executed in debug mode. In release build, the assert disappears entirely, and the condition is never evaluated. (So if you call a function or whatnot inside of assert(<blah>), it'll never get called)
 
I make the same mistakes when I try to do multiple operations in a single statement. So I don't do that. Use plenty of vars, restrict statements to single operations, and make sure your functions are short and understandable at a glance.

It won't really matter for the speed anyway, the compiler often optimizes many different pieces of source code to the same machine code, as long as the algorithm is the same. Try debugging something with all optimizations on: many variables and lines of code don't exist, and most counters count backwards to zero.

So, don't try to optimize things yourself. Make it readable and understandable. And test all your parameters and data to see if they are valid before using them.
 
Thanks guys, this discussion helped a lot.

~That electric fence thing in retrospect, would have saved me *hours* back when I was in college taking advanced data processing. Fun fun!
 
Last edited by a moderator:
Beware the disappearing assert() problem though. The 'condition' statement is only executed in debug mode. In release build, the assert disappears entirely, and the condition is never evaluated. (So if you call a function or whatnot inside of assert(<blah>), it'll never get called)
Surely that's only if you explicitly define the "NDEBUG".

I generally have it enabled in optimised builds (whilst still testing it) and disable it only for a final release.
 
Surely that's only if you explicitly define the "NDEBUG".

I generally have it enabled in optimised builds (whilst still testing it) and disable it only for a final release.

Yes, that's more accurate than 'release' or 'debug' (which may be different on different tools), even though generally release/debug turns on/off the NDEBUG flag.
 
Back
Top