To find an error in the code you just need...
$$$\text{ }$$$Firsly, you can check overflow of integer types, mistakes like =
instead ==
, writing a long long
value in int
and something else. It can be done in compile time with enabling of all available warnings. You can check your code fast there. Link have examples and you can read them. Warnings is not an errors, but sometimes can be.
Description of warnings can be found there.
List of used warnings-Wall -Wextra -pedantic -std=c++17 -O3 -Wshadow -Wformat=2 -Wfloat-equal -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
Secondly, you can test your solution in custom invocation. You should go to Custom invocation, place your code and test on samples and custom testswith compiler Clang++17 Diagnostics. It is a magic, just try a few examples:
Integer overflow in multiplication#include <bits/stdc++.h>
int main() {
int a, b; std::cin >> a >> b;
std::cout << a * b << std::endl;
}
On testcase 10000000 10000000
you will see nexr: runtime error: signed integer overflow: 10000000 * 10000000 cannot be represented in type 'int'
Integer overflow in left shift#include <bits/stdc++.h>
int64_t val, res(0);
int main() {
std::cin >> val;
for (int bit = 0; bit < 60; bit++) {
if (val & (1 << bit)) res++;
}
std::cout << res << std::endl;
return 0;
}
Testcase 1000000000000
. You will see next:
runtime error: shift exponent 32 is too large for 32-bit type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
Out of bounds vector or array#include <bits/stdc++.h>
int main() {
int arr[10];
int n; std::cin >> n;
for (int i = 0; i < n; i++) {
std::cin >> arr[i];
}
}
Test: 11 0 1 2 3 4 5 6 7 8 9 0
You will see next:
WRITE of size 4 at 0x1126fd98 thread T0
#0 0xcb1d96 in std::bas...
Next steps: with GNU G++ compiler use a debug version of stardard library with next macroses:
#define _GLIBCXX_DEBUG 1
#define _GLIBCXX_DEBUG_PEDANTIC 1
#define _FORTIFY_SOURCE 2
You should to run a few tests for your code to catch mistakes in runtime. Examples:
Out of bounds vector#define _GLIBCXX_DEBUG 1
#define _GLIBCXX_DEBUG_PEDANTIC 1
#define _FORTIFY_SOURCE 2
#include <bits/stdc++.h>
int main() {
std::vector<int> arr(3,1);
std::cout << arr[3];
}
You can see next:
Error: attempt to subscript container with out-of-bounds index 3, but
container only holds 3 elements.
It is more useful and have a detailed description in comparison with Clang++17 Disgnostics
Run it
Attempt to dereference a singular iterator#define _GLIBCXX_DEBUG 1
#define _GLIBCXX_DEBUG_PEDANTIC 1
#define _FORTIFY_SOURCE 2
#include <bits/stdc++.h>
int main() {
std::set<int> set{1,2,3,4,5};
auto it = set.begin();
set.erase(set.begin());
std::cout << *it << std::endl;
}
We try to save needed iterator, erase it and forgot about it. Then, when we will output a value of iterator, will see next:
Error: attempt to dereference a singular iterator.
Run it
Merge two partially sorted arrays#define _GLIBCXX_DEBUG 1
#define _GLIBCXX_DEBUG_PEDANTIC 1
#define _FORTIFY_SOURCE 2
#include <bits/stdc++.h>
#define all(x) (x).begin(),(x).end()
using vi = std::vector<int>;
int main() {
vi left = {1,2,3};
vi right = {3,1,2};
vi res;
std::merge(all(left), all(right), std::back_inserter(res));
}
In this example we use standard algo for merging two sorted arrays, but we forgot to sort one of them. No problem, G++ will print next error:
Error: elements in iterator range [__first2, __last2) are not sorted.
Run it
Unexpected reallocation of memory in vector#define _GLIBCXX_DEBUG 1
#define _GLIBCXX_DEBUG_PEDANTIC 1
#define _FORTIFY_SOURCE 2
#include <bits/stdc++.h>
#define all(x) (x).begin(),(x).end()
using vi = std::vector<int>;
int main() {
vi arr{1,2,3,4};
auto ptr = arr.begin();
arr.push_back(5);
std::cout << *ptr << std::endl;
}
Ok, we forgot, that in resizing of vector if capacity is smaller then needed size, all items will be copied in new allocated memory, old memory wil be deleted. And when we will try to access a deleted memory, will see next:
Error: attempt to dereference a singular iterator.
Run it
If you are solving a problem, try to test it by yourself on your small testcases, or brute all testcases and compare results of fast algorithm with results of naive but correct algorithm.
Also, you can use assert(condition);
for check invariants or conditions. Even if you think that it will be always correct, because in practice small mistake in logic will leads to assertion failures. If the condition is false, you will see verdict "Runtime error" for you submission.
First example of using assert#include <iostream>
#include <cassert> // для assert
int main() {
int a, b; std::cin >> a >> b;
// calculate a difference
int c = a - b;
// check, that difference is calculated right: c = a - b ==> c + b == a
assert(c + b == a);
std::cout << c << std::endl;
Second example of using assert#include <iostream>
#include <cassert> // для assert
int f(int n) {
// check that argument is always positive integer or zero
assert(n >= 0);
if (n == 0) return 1;
return n * f(n - 1);
}
int main() {
int n; std::cin >> n; std::cout << f(n);
Next, you can use a method called Method of intent look: when you try to detect what is wrong by reading you code. You should write a easy readable code for it.
Also, you can try to output some variables that u use in program. These macroses will help efficiently output a debug information in console, for example, variables with their names, containers like std::vector<X>
and std::set<X>
, pairs like std::pair<X,Y>
. You can extend their functionality, or now you know that it is possible and can search for a better macroses or creater it by yourself.
I recommend to read this blog about catching silly mistakes in GCC C++.