Debugging
It is very common when solving a task that your program does not behave exactly as expected. The process of figuring out what went wrong and fixing it is called debugging. There is no “best” way to debug a program but there are techniques which help you find the mistake faster.
Code Style
Cleaner codes are less likely to produce errors. Make sure you name your variables properly so that you don’t mix them up. Also, use proper indentation and spacing to enhance the readabilty for future debugging.
Bad code:
#include<iostream>
#include<cmath>
#include<cassert>
#include<iomanip>
#include<algorithm>
using namespace std;
int main () {
int a,b;
cin>>a>>b;
int n[a];
for(int m=a-1;m>-1;m-=1){
cin>>n[m];
}
cout << n[b];}Good code:
#include<iostream>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
int a[n];
for (int i = n - 1 ; i >= 0 ; i--) { // inputting the array from right to left
cin >> a[i];
}
cout << a[m];
} - Included only the needed libraries, don’t use massive templates.
- Use
nandmfor sizes or counts — this is the convention in most programming tasks, making your code easier to read and understand. - Use
i,j,kfor iteration variables — these are standard names used in loops. - Indent consistently: each level of nesting should be indented by one tab (or 4 spaces).
- Unindent when closing a block — this helps you visually see where a loop or condition ends.
- Leave comments if needed to understand what you wanted to do and compare it with what you actually did.
Printing
The idea is to use cout to keep track of our variables. If we notice that a variable has an unexpected value it might give us a clue on what caused that to happen.
Say we are trying to print an array in reverse:
int a[5] = {1, 2, 3, 4, 5};
for (int i = 4 ; i >= 0 ; i++) {
cout << a[i] << ' ';
}After running the program we see something like this:
Output:
5 0 -1150222592 1577706123 1165962832 32767 126...
That does not look right…
Lets print the value of i before each print statement to see what index is actually being printed.
int a[5] = {1, 2, 3, 4, 5};
for (int i = 4 ; i >= 0 ; i++) {
cout << "index 'i' is: " << i << '\n';
cout << a[i] << '\n'; // change this to \n to make the output more readable
}Output:
index 'i' is: 4
5
index 'i' is: 5
0
index 'i' is: 6
1781710592
index 'i' is: 7
1056066187...
We expect the value of i to be 4, 3, 2, 1, 0 but instead it is increasing. The expression for updating i is the increment expression in the for-loop. It should say i-- but instead it says i++ and that was the bug.
int a[5] = {1, 2, 3, 4, 5};
for (int i = 4 ; i >= 0 ; i--) {
cout << a[i] << ' ';
}Output:
5 4 3 2 1
Assert
The function assert accepts a boolean expression as an argument and if the expression evaluates to false the program terminates. On most judges this termination produces the verdict Runtime Error. This is useful when a program has multiple branches and is producing another verdict.
To use this function we must include the cassert library.
For example, we have a task which says: Given an integer \(N\), evaluate \(1 + 2 + ... + N\).
#include<iostream>
using namespace std;
int fast (int n) {
return n * (n + 1) / 2;
}
int main () {
int n;
cin >> n;
cout << fast(n);
}When we submit this program we get Wrong Answer. We might have another solution to this problem which we are more sure of but might be too slow to pass, for example looping over the values and summing them up.
Then we can assert if slow and fast produce the same values.
#include<iostream>
#include<cassert>
using namespace std;
int slow (int n) {
int ans = 0;
for (int i = 1 ; i <= n ; i++) ans += i;
return ans;
}
int fast (int n) {
return n * (n + 1) / 2;
}
int main () {
int n;
cin >> n;
assert(fast(n) == slow(n));
cout << fast(n);
}This has two possible outcomes, either we get Time Limit Exceeded meaning we probably pass the small tests, or we get Runtime Error meaning they produce different results.
If we get get Runtime Error we can look into the formula and make sure its correct, but if we get Time Limit Exceeded then we can deduce that our formula fails on large numbers which means it can be an overflow issue.
Other tips
Output & Formatting
- Ensure the output format exactly matches the problem statement (no extra spaces or lines).
- Remove all debug prints before submitting.
Input & Corner Cases
Handle all edge cases (e.g.
N = 0,N = 1, empty inputs, max constraints).For problems with multiple test cases, reset all variables and containers between tests.
- Bugs often appear when a small test follows a large one.
Problem & Code Review
- Re-read the problem statement carefully.
- Re-read your code — common mistakes: mixing up
N/M,i/j, etc. - Watch for shadowed, unused, or uninitialized variables.
Undefined Behavior
- Uninitialized variables
- Array/vector out-of-bounds access
- Missing return from non-void functions
- Signed integer overflow (use
long long) - Bit-shifting by ≥ 32 bits on 32-bit integers
Floating Point
- Check for
NaNs (e.g.sqrtof negative). - Use
long doublefor extra precision if needed. - Match the required precision in output (
setprecision()).
Testing
- Perform manual walkthroughs on simple cases.
- Stress test your code: compare results of your fast solution vs. a slow brute-force version.
Runtime Error
- Any undefined behavior?
- Any assertions that might fail?
- Any possible division by
0? (mod0for example) - Any possible infinite recursion?
- Invalidated pointers or iterators?
- Are you using too much memory?
Time Limit Exceeded
- Do you have any possible infinite loops?
- What is the complexity of your algorithm?
- Did you remove debug output before submitting (ex. are you printing a lot of information to standard error)?
- Unnecessary copying of data? Consider passing variables by reference.
- Try substituting arrays in place of vectors.
Last Resort
- Rewrite your solution from the start.
- Be sure to save a copy of your original solution. It’s always possible that you might introduce more bugs in your new solution.