7.14 — Common semantic errors in C++

In lesson 3.1 -- Syntax and semantic errors, we covered syntax errors, which occur when you write code that is not valid according to the grammar of the C++ language. The compiler will notify of you these types of errors, so they are trivial to catch, and usually straightforward to fix.

We also covered semantic errors, which occur when you write code that does not do what you intended. The compiler generally will not catch semantic errors (though in some cases, smart compilers may be able to generate a warning).

Semantic errors can cause most of the same symptoms of undefined behavior, such as causing the program to produce the wrong results, causing erratic behavior, corrupting program data, causing the program to crash -- or they may not have any impact at all.

When writing programs, it is almost inevitable that you will make semantic errors. You will probably notice some of these just by using the program: for example, if you were writing a maze game, and your character was able to walk through walls. Testing your program (7.12 -- Introduction to testing your code) can also help surface semantic errors.

But there’s one other thing that can help -- and that’s knowing which type of semantic errors are most common, so you can spend a little more time ensuring things are right in those cases.

In this lesson, we’ll cover a bunch of the most common types of semantic errors that occur in C++ (most of which have to do with flow control in some way).

Conditional logic errors

One of the most common types of semantic errors is a conditional logic error. A conditional logic error occurs when the programmer incorrectly codes the logic of a conditional statement or loop condition. Here is a simple example:

Here’s a run of the program that exhibits the conditional logic error:

Enter an integer: 5
5 is greater than 5

When the user enters 5, the conditional expression x >= 5 evaluates to true, so the associated statement is executed.

Here’s another example, using a for loop:

This program is supposed to print all of the numbers between 1 and the number the user entered. But here’s what it actually does:

Enter an integer: 5

It didn’t print anything. This happens because on entrance to the for loop, count > x is false, so the loop never iterates at all.

Infinite loops

In lesson 7.7 -- Intro to loops and while statements, we covered infinite loops, and showed this example:

In this case, we forgot to increment count, so the loop condition will never be false, and the loop will continue to print:

1 1 1 1 1 1 1 1 1 1

until the user shuts down the program.

Here’s another example that teachers love asking as a quiz question. What’s wrong with the following code?

This program is supposed to print 5 4 3 2 1 blastoff!, which it does, but it doesn’t stop there. In actuality, it prints:

5 4 3 2 1 blastoff! 4294967295 4294967294 4294967293 4294967292 4294967291

and then just keeps decrementing. The program will never terminate, because count >= 0 can never be false when count is an unsigned integer.

Off-by-one errors

An off-by-one error is an error that occurs when a loop executes one too many or one too few times. Here’s an example that we covered in lesson 7.9 -- For statements:

This code is supposed to print 1 2 3 4 5, but it only prints 1 2 3 4 because the wrong relational operator was used.

Incorrect operator precedence

From lesson 5.7 -- Logical operators, the following program makes an operator precedence mistake:

Because logical NOT has higher precedence than operator>, the conditional evaluates as if it were written (!x) > y, which isn’t what the programmer intended.

As a result, this program prints:

5 is greater than 7

This can also happen when mixing Logical OR and Logical AND in the same expression (Logical AND takes precedent over Logical OR). Use explicit parenthesization to avoid these kinds of errors.

Precision issues with floating point types

The following floating point variable doesn’t have enough precision to store the entire number:

As a consequence, this program prints:


In lesson 5.6 -- Relational operators and floating point comparisons, we talked about how using operator== and operator!= can be problematic with floating point numbers due to small rounding errors (as well as what to do about it). Here’s an example:

This program prints:

not equal

The more arithmetic you do with a floating point number, the more it will accumulate small rounding errors.

Integer division

In the following example, we mean to do a floating point division, but because both operands are integers, we end up doing an integer division instead:

This prints:

5 divided by 3 is: 1

In lesson 5.2 -- Arithmetic operators, we showed that we can use static_cast to convert one of the integral operands to a floating point value in order to do floating point division.

Accidental null statements

In lesson 7.3 -- Common if statement problems, we covered null statements, which are statements that do nothing.

In the below program, we only want to blow up the world if we have the user’s permission:

However, because of an accidental null statement, the function call to blowUpWorld() is always executed, so we blow up it regardless:

Should we blow up the world again? (y/n): n

Not using a compound statement when one is required

Another variant of the above program that always blows up the world:

This program prints:

Should we blow up the world again? (y/n): n

A dangling else (covered in lesson 7.3 -- Common if statement problems) also falls into this category.

What else?

The above represents a good sample of the most common type of semantic errors new C++ programmers tend to make, but there are plenty more. Readers, if you have any additional ones that you think are common pitfalls, leave a note in the comments.

7.15 -- Detecting and handling errors
7.13 -- Code coverage

18 comments to 7.14 — Common semantic errors in C++

  • ChebCheb

    What about accidentally using/changing the wrong Variables?

    Like if you pass to a function the amount of apples, bananas and pineapples you've baught as arguments to become variables a,b and c, and the function has the corresponding prices initialized as variables pa, pb and ppa (like ppa=priceof-pine-apple). You could easily accidentally type in pa instead of ppa, or even misunderstand your own naming logic and think pa stands for pineapple, not priceof-apple. That way, you can easily mix things up and the function ends up calculating the wrong numbers.
    The same way, a "buy fruits"-function could accidentally increase the number of one fruit whenever you buy a certain different fruit.
    That's the main reasons why i always prefer varible names that leave no doubt on what they mean and dislike names like "x". "DistanceYouveTraveledSinceTheLastTankStop" is an awkward name, but you can't possibly get it wrong :D
    Or is this not considered a semantic error?

  • ewrilan

    Idk is it a common mistake, but i often write code like this:

    number_of_ones(5) should result in two, but gives 5. Thats because it should be

    P.S. You should write in O.2 Bitwise operators, that lshift and rshift are faster alternatives to multiplying and dividing by powers of 2, it's one of their most important values.

    • Alex

      Any modern compiler worth its salt should optimize divisions or multiplications by a power of 2 into the corresponding shifts. Some go even further (there is a good example of how a compiler decomposes dividing by 71 here:

  • Volatus

    The last part of the tutorial has a repeated word:
    The above represents a good sample [[of of]] the most common type of semantic.

  • Forhad Rahman

    In x{5}, instead of using static cast, writing x{5.0} will also do the same job. So, any problem if I don't use static cast and write as x{5.0}? Or it doesn't considered as best practice?

  • Spero

    I don't know if it's a common mistake but an easy one to make that the complier might not pick up on is:

    instead of

    which can produce unexpected results while not pointing to the location of the error.

  • Waldo Lemmer

    Great lesson, thanks :D

    First paragraph:
    > The compiler will notify of you these type of errors
    "type" should be "types".

    Section "Incorrect operator precedence", last sentence:
    > Use explicit parenthesization to avoid these kind of errors.
    "kind" should be "kinds".

  • I have just passed through one, which may be a mix of some.
    When increasing or decreasing counters in loops or if statements, be aware of the location where the increment/decrement occurs.

    This code was leading me to print "You have 0 lives left.". After correction, it's changed to:

    Now I decrease the Lives and compare: if it is higher than 0, the message of how many lives is generated and the new prompt to guess executes.
    If Lives == 0, then it's game over.

  • Fan

    What about writing if (x=0) instead of if (x==0)? Is that semantic or syntactic? What do you think about the suggestion of writing if (0==x)?

    Also it helps to point out that turning on compiler warnings can catch some of the errors.

    • Alex

      Using assignment instead of comparison is a semantic error. It will compile fine, but it won't do what you want.

      The advantage of (0==x) is that if you accidentally use assignment instead, then it won't compile, since you can't assign to 0.

      The disadvantage is it doesn't map to how we talk about things (we usually say "compare x to 0", not "compare 0 to x").

      Personally, I think either is fine.

  • Andreas Krug

    Small suggestion: Testing your program (7.12 -- Introduction to testing your code)  instead of  Testing your program (6.11 -- Scope, duration, and linkage summary)

Leave a Comment

Put all code inside code tags: [code]your code here[/code]