The C++ bugs I love. ❤
A curated list of common C++ pitfalls that you should avoid learning about the hard way.
While C++ is my favourite programming language for reasons that range from complete control, low-level memory access and the underlying trust-the-programmer philosophy of the language design, these are the very same reasons that have given me bugs that I have lost my sleep over and cost me hundreds in codeforces’ negative delta. While these may look childish at first, when you are writing full-fledged code, these will be hard to isolate and will cost precious time that could otherwise be used in problem-solving and… well you get the point, these are important. As a beginner, I would have loved to get a heads up about these. Do try to answer these questions yourself first before moving on to the explanations. Happy reading
All inputs are given through
STDIN
input stream and all outputs are written toSTDOUT
output stream
- Shallow vs. deep copies
Determine the output
STDOUT:
100
100
Modifying the value t.a[0]
modifies the value r.a[0]
automatically. This is an easy exploit of shallow vs. deep copies. By default, the assignment operator =
copies only the statically allocated values. While the integer pointer a
is statically allocated, the array it points to is dynamically allocated. The solution here is to create a copy constructor. While this is easy to isolate, it’s easier to miss in the heat of a problem-solving frenzy.
- Varying string operation complexity
what is the overall complexity of the following snippet
Statement 1 and 2 have a complexity of O(n)
where n
is the length of the original string while statement 3 has a complexity of O(1)
. std::string
, under the hood, implements a vector<char>
. When we do str + 'a'
or 'a' + str
C++ allocates new memory equivalent to the sum of sizes of str
and 'a'
then copies the entire string and the character you’re trying to concatenate into this new memory in the needed order, thus costing linear time. whereas str+='a’
simply doesstr.push_back('a')
in the +=
operator’s implementation. classic operator overloading. Thus overall complexity would turn out to be O(n)
.
- Integer rollover
Determine the output.
STDOUT:
Nay
In C++, functions like string::lenth()
, vector::size()
and such return a value of special size_t
datatype. size_t
is always unsigned since attributes like size
and length
cannot be negative. Thus when trying to comparej
which is -1
with a.size()
, j
gets implicitly typecasted to size_t
and rolls over to a really big number 18446744073709551615
thus giving the undesired result.
- Double comparison
Determine the output
STDOUT:
Nay
As a general rule of thumb, never do double comparisons with the ==
operator. A little debugger inspection would reveal that value of a, b, c
is 0.10000000000000001, 0.20000000000000001, 0.29999999999999999
which will obviously not evaluate a + b == c
to logical true. The right way to go about doing double comparisons would be to check for error tolerances. E.g. the above conditional should be written as abs((a + b)-c) < 1e-9
where 1e-9
is the acceptable tolerance in the result.
- Homogeneity in ternary operators
Determine the output
The above code will result in a compilation error. C++ is a statically typed language, that is the data type of every variable and the return type of every expression must be known at compile-time itself. The ternary expression cannot return a char*
and an int
depending on runtime calculations. Thus a ternary operator’s return expressions should always be homogenous.
- Stray newline character
Determine the output based on STDIN inputs
STDIN:
2
Yay
NaySTDOUT:
Yay
Saved the best one for the last. When our code is supposed to take two strings as input and print them as output, it prints only one. Normal cin
delimits its inputs on whitespace and sends the contents of the input buffer(temporary storage area of data) when the return
key(enter) is pressed; whereas getline()
modifies cin
such that it delimits on newline ('\n'
) character. This is useful when we want to input strings that contain whitespace. Our Enter key serves both purposes. Now, when we input, say an integer, through normal cin
, contents that were typed before the enter key was pressed are passed on to our program. However, the '\n'
character is left behind in our input buffer. Then, during the first while
loop iteration, when our program runs the getline(cin, str)
the first thing that is encountered is the leftover newline character and getline()
thus sends everything that was before this character i.e. nothing. An empty string. After that, in the second iteration, it normally inputs the next string Jon
and prints it out.
The solution to getting the desired result here is to use a cin.ignore()
before getline()
. This function ignores everything from the last input, in our case a stray newline, and our code runs as it should.
While the list is clearly not exhaustive, I hope it does provide hints as to what details you need to look out for when you encounter an erratic bug. I would love to hear your favourite bugs if you have any.