…but they should.
When I say "testcases", I don't mean a unit test, or even a functional test, or any set of instructions to verify the correctness of a program.
I'm talking about the reduced example you create when you're debugging a program you already know to be functioning incorrectly.
Let me demonstrate.
A Stack Overflow user, memorably named user103260, posted the following code, baffled that it was not producing the result he/she/it expected:
#include
using namespace std;
int main(){
int y;
unsigned long long int pos;
cin>>y;
cin>>pos;
int f=0;
while(y>0){
y–;
if(pos&1==0){
f=1-f;
}
pos=pos/2;
}
if(f==1){
cout
I was coding and the following code doesn't give the desired output.
pos&1
is supposed to return the remainder whenpos
is divided by 2. When I replacepos&1
bypos%2
everything works just fine. What could be the problem?
In going to answer, my first thought is that his expectation is valid: pos&1
and pos%2
should, indeed, be functionally equivalent in this case. My second thought is that there is a lot of code here, and it's clearly his "real", "full" program; it's hard to see how taking input, giving output, or dividing anything by two could be related to the problem at hand.
What this programmer is missing is a basic programming skill and unfortunately that's a trend very much on the rise. Empirically, it seems likely that program debugging is not being correctly taught in many education institutions and that, at best, those undergoing self-teaching have little way to even know about this little "trick".
It's called divide and conquer.
Put simply, in all but the rarest of occasions, my very first debugging step is to create a new, fresh C++ project, then selectively add minimal bits of functionality until I can reproduce the problem. No user input, no more output than is needed to tell me what the problematic code is doing, no large datasets (unless they are part of the problem), no complex conditionals, no functions that we don't need…
By isolating the troubling code, you can very quickly home in on your issue.
How to form a testcase
In this case, the programmer has already realised that the problem lies with &
vs %
, and his next step should have been writing the following new program:
#include
int main()
{
std::cout
By composing this testcase (or Short, Self-Contained Correct Example, for short), we are immediately testing how &
and %
behave, and how these behaviours may differ from our initial expectations. This isn't rocket science — we're just being diligent and testing in isolation.
The result of the above code is:
1, 0, 1, 0
1, 0, 1, 0
We didn't provide many inputs so we don't have a wealth of data to go on, but already we can see that the two operators likely are performing equivalently, so we can put our doubts aside and add the next piece of logic from our original program to see where we really went wrong.
In this case, next up is the ==
:
#include
int main()
{
std::cout
The output is:
0, 0, 0, 0
0, 1, 0, 1
Now we're talking. There is a clear difference in the output, and we can go away and figure out what that is, satisfied that our question is really about how pos&1==0
differs from pos%2==0
.
As it turns out, this is a completely different question, and the answer is that ==
has a higher precedence than &
so the code is equivalent to:
[source:cpp]
#include
int main()
{
std::cout
This is obviously not what we intended, so we need only introduce some parentheses to express our true intent, and voilĂ :
[source:cpp]
#include
int main()
{
std::cout
0, 1, 0, 1
0, 1, 0, 1
Now by simply adding a single set of parentheses to one line in his original program (if ((pos&1)==0)
), the entire problem is solved and understood.
Like any set of steps you're given, this all probably sounds like a bit of a drag, especially when you can simply pop along to Stack Overflow and have me and my friends fix it for you in moments. But if you're serious about programming, and you want to be a programmer, learning to debug is absolutely crucial. When you get past beginner books and you're writing real-life applications, let's just say you're not going to be able to post 50,000 lines of code onto Stack Overflow. You need to learn how to debug.
And it's not a drag; the above testcase can be constructed and run in less time than it took to read this article. The warm, fuzzy feeling that's subsequently imbued upon your soul is priceless: it always feels best to solve your own problems.
What a testcase is not
Note, though, that this is not the same as posting your original code on the internet with some lines semi-randomly removed, or replaced with the text ...
. This isn't helpful. All you've done now is introduced two new problems:
- Your program no longer compiles, if it did before;
- You may have hidden the cause of the problem.
If you don't know which part of your program is not working, then you are not qualified to decide which parts are relevant. Instead you should create a testcase (which, I'll say again, is a different program, simpler with less code, which nonetheless demonstrates the exact same problem). You discover for sure which lines are relevant as you debug with the testcase, finding out which combination of code reproduces the problem. You can then be helped to fix the testcase, and you can apply the knowledge gained therein to your real code.
Conclusion
I hope that universities and books can begin teaching this critical debugging skill, because it's a concern if the next generation of programmers, in an ever-increasingly computer-controlled world, cannot debug code without spamming it onto the internet for hand-outs. A real concern.