Search

3.x — Chapter 3 summary and quiz

Quick Summary

A syntax error is an error that occurs when you write a statement that is not valid according to the grammar of the C++ language. The compiler will catch these.

A semantic error occurs when a statement is syntactically valid, but does not do what the programmer intended.

The process of finding and removing errors from a program is called debugging.

We can use a five step process to approach debugging:

  1. Find the root cause
  2. Understand the problem
  3. Determine a fix
  4. Repair the issue
  5. Retest

Finding an error is usually the hardest part of debugging.

Static analysis tools are tools that analyze your code and look for semantic issues that may indicate problems with your code.

Being able to reliably reproduce an issue is the first and most important step in debugging.

There are a number of tactics we can use to help find issues:

  • Commenting out code
  • Using output statements to validate your code flow
  • Printing values

When using print statements, use std::cerr instead of std::cout. But even better, avoid debugging via print statements.

A log file is a file that records events that occur in a program. The process of writing information to a log file is called logging.

The process of restructuring your code without changing what it actually does is called refactoring.

unit testing is a software testing method by which small units of source code are tested to determine whether they are correct.

Defensive programming is a technique whereby the programmer tries to anticipate all of the ways the software could be misused. These misuses can often be detected and mitigated.

All of the information tracked in a program (variable values, which functions have been called, the current point of execution) is part of the program state.

A debugger is a tool that allows the programmer to control how a program executes and examine the program state while the program is running. An integrated debugger is a debugger that integrates into the code editor.

Stepping is the name for a set of related debugging features that allow you to step through our code statement by statement.

Step into executes the next statement in the normal execution path of the program, and then pauses execution. If the statement contains a function call, step into causes the program to jump to the top of the function being called.

Step over executes the next statement in the normal execution path of the program, and then pauses execution. If the statement contains a function call, step over executes the function and returns control to you after the function has been executed.

Step out executes all remaining code in the function currently being executed and then returns control to you when the function has returned.

Run to cursor executes the program until execution reaches the statement selected by your mouse cursor.

Continue runs the program, until the program terminates or a breakpoint is hit.
Start is the same as continue, just from the beginning of the program.

A breakpoint is a special marker that tells the debugger to stop execution of the program when the breakpoint is reached.

Watching a variable allows you to inspect the value of a variable while the program is executing in debug mode. The watch window allows you to examine the value of variables or expressions.

The call stack is a list of all the active functions that have been executed to get to the current point of execution. The call stack window is a debugger window that shows the call stack.

Quiz time

Question #1

Use the integrated debugger to step through this program and watch the value of x. Based on the information you learn, fix the following program:

Show Solution

Question #2

Use the integrated debugger to step through this program. For inputs, enter 8 and 4. Based on the information you learn, fix the following program:

Show Solution

Question #3

What does the call stack look like in the following program when the point of execution is on line 4? Only the function names are needed for this exercise, not the line numbers indicating the point of return.

Show Solution

Author's note

It’s hard to find good examples of simple programs that have non-obvious issues to debug, given the limited material covered so far. Any readers have any suggestions?


4.1 -- Introduction to fundamental data types
Index
3.10 -- Finding issues before they become problems

36 comments to 3.x — Chapter 3 summary and quiz

  • HolzstockG

    I did the first one a little bit easier by using expression inside of curly braces of "x" variable

    • chai

      you don't want to get into a habit of this. The problem is the machine does't know which function call is executed first. Might not be a problem now but may be later on. Not a good practice.

  • Jake

    Hi Alex.  Another wonderment at not being properly stopped, but this time at the run level.

    In question 2, line 22, we are obviously supposed to run into the zero-divide error.  However, if I run without debugging, or run it in a CMD window, all I get is the standard Windows error box "Program Quiz-3.2 has stopped working". The only time I see the "Unhandled Exception" box is if I try to step over the

    line.

    Why won't is kick me out the way it would in Unix?  OK, maybe not the crash dump but at least a zero-divide error croak message! (I am trying to provoke the bouncer.  I'm SO sociopathic today!  :-)

  • Jake

    Question, Alex. (Yuh think? :-)

    Since we defined the readNumber() function as returning an int, why didn't I get a compile error, or at least a warning, at line 13, where we are discarding the return value and treating it like a void function?  Of course I saw this by eyeball but even with the "treat warnings like errors" setting (as well as level 4), I got no error.

    Thanks for this tutorial!

    • nascardriver

      Hi Jake,

      it's perfectly legal to discard the return value of a function. For `readNumber`, it doesn't make sense to discard it, because the whole point of the function is to produce the value that it returns.
      There are functions that return values we might not care about, because their purpose is something else and the return value may or may not be relevant.
      When you write a function like `readNumber`, where discarding the return value is considered to be a mistake, mark it as `[[nodiscard]]`.

      `[[nodiscard]]` tells the compiler that you want a warning to be printed if the return value is discarded.

      • Jake

        WOW!  In all the C++ books I have, I found one mention of [[nodiscard]] in the Stroustrup book "A Tour of C++
        Second Edition", page 216,  without a description of what that does.  I'm using the community version of Visual Studio 19 and when I added [[nodiscard]] that to the code I got this error message:

        On the other hand, I do have a complete Cygwin environment and when I compiled it with that release of g++ I did get the expected error message:

        But it still produces an executable.  And when I ran that executable with the bad code I got the error I had originally expected:

        Floating point??  No mention of zero-divide? OK, not exactly the expected error message.

        This has been VERY instructive, although I'm not totally ready to start using the Cygwin environment for this tutorial.

        • nascardriver

          `[[nodicard]]` asks the compiler to produce a warning, not an error. If you want an error, you need to treat Wunused-result as an error

          VS should support `[[nodiscard]]` too, you just didn't set up your project correctly. In you project's settings, enable the highest standard support that's available (Should be C++17). `[[nodiscard]]` was added in C++17.

  • Mike

    For Quiz #2, I copied and pasted your sample code twice now, and it compiles and executes, but as soon as I press enter after entering the second number, it crashes every time with a Windows error that my test.exe has stopped working. One of my options is "Debug the Program" which I thought was fitting considering this chapter. I tried it, but it seems way to advanced for me.

    Any suggestions as to why this is happening? BTW: I'm using Visual Studio 2019.

    The Exception code is c0000094, if that helps. I just googled it. Appears it may be related to dividing by zero. But why, I entered 8 and 4 as per your instructions?

    • It's supposed to crash. The purpose of the quiz is that you figure out why. You can solve it by reading the code too, but you won't learn how to use the debugger that way.

      • Mike

        Ha! too funny! I guess I just wasn't expecting a full blown crash of the program at this point, though it makes perfect sense. Can't wait to try to debug it now!

        Sorry for wasting your time on this one.

  • Jose

    Hi, question #3 shows the functions sorted this way:

    but if I had to write that code I would have sorted them this way:

    Is there any rule/best practice about sorting the functions called from main()?

  • Joe Das

    Outstanding Tutorial. You are really helping me get my old coding chops back so quickly, and your discussion points about good working practices are invaluable.

    You were asking about nasty problems in debugging: An example which will quickly aggravate coders but which does happen is when a variable is misspelled because it is declared twice by similar names and the wrong variable is used in a function. This would tie back into your recommendations about variable naming. This can be made even nastier with confusing pre-processor statements >:D.

    • Alex

      > An example which will quickly aggravate coders but which does happen is when a variable is misspelled because it is declared twice by similar names and the wrong variable is used in a function

      Agreed -- that's pretty similar to quiz question #2, no?

  • Lawrence

    Why can't I add enter a value when I am stepping in during debugging?

  • NotJoeRogan

    Hello Alex or Nas,

    Let's say that I enter a breakpoint at line 7 of the program in the first question, and for the number I enter 4. Why is it that when I hover my cursor over all of the x variables throughout the entire program, they show a value of 4?

    Regards,
    NotJoeRogan

    • Alex

      When you hover over a variable, the debugger will show you the _current_ value for that variable, not the value the variable might have wherever you are hovering. This can be misleading the variable had a different value at the point you're hovering

      To do otherwise would require the debugger to remember what values every variable had at every point in the program, which would be significantly more complex, and potentially misleading in other ways (you might think you're seeing the current value when you aren't).

  • Maura

    Please better explain on Question #3 why the answer for the call stack is
    d
    b
    a
    main

    and not instead
    d
    c
    b
    a
    main

    • Jason

      Because when the stack reaches function b, it begins it execution.

      First it reaches the call to function c.
      At this point the stack is c/b/a/main.
      However c does not make any calls and fully resolves itself.
      At which it returns to the next object in the stack b, and moves on to the call to function d.
      Thus the stack is d/b/a/main

    • Alex

      main calls a()
      a calls b()
      b calls c()
      c terminates
      b calls d()
      At this point we've hit our breakpoint.

      Working backwards, the functions still in memory are:
      d
      b
      a
      main

      c isn't in the list because it terminated and was removed from the call stack before d was called.

  • John Doe

    What about leveraging your example of function argument order of evaluation issues in chapter 2.3? It would be a weird scenario but would force the user to step through while debugging and see the flow issues in their stack. Testing in visual studio and gcc, this processed right to left, but clang did left to right.

    • Alex

      Good thought! However, the program would only manifest the problem on some machines and not others, making it a hard one to debug on machines that didn't exhibit the issue.

  • Louis Cloete

    Debugging exercise ideas: use "/n" for a newline somewhere instead of "\n" and watch the ensuing chaos when /n prints on the screen... ;-p

    Do something like this:

    [This next idea I borrow from my Delphi textbook. You will have to wait till after you've done loops, strings and arrays for this one.]

    The output line only executes once, because of missing braces around the intended loop body. The function prints only the last character of the string.

    • Hi!

      > Uninitialised variable causes undefined behaviour
      The value of @x is undefined, behavior is well defined.

      > next idea
      @s should be const and @i should be an @std::size_t (Or cast s.length()).
      This code wouldn't compile, because line 9 cannot see @c.
      I like this one. Even though compilers print a warning, it's a common mistake.

      • Louis Cloete

        About prtStrVertical():

        In Pascal, you must define all local variables for a function or procedure (Pascal's name for a group of statements that doesn't return a value) at the top of the function, so the scoping issue doesn't appear. To work around, either declare char c before the loop, or modify the function like this:

        The modification will cause a different, but related, bug.

    • Alex

      Thanks for the thoughts! The '/n' idea should be easy to diagnose by inspection. I'm trying to find things that are not quite as obvious to inspection, to validate the usefulness of having an integrated debugger.

      There are a ton of fun debugging problems once we do loops -- but alas, that chapter is a bit far off from this lesson. I didn't want to wait that long to introduce basic debugging topics.

  • Louis Cloete

    Question #1 solution:

    readNumber():

    main():

    l. 18:

    Question #2:

    The problem could've been avoided if the program didn't declare and zero init variables, just to assign different values to them in the first place. A better solution would be to init x and y with the result of separate calls to readNumber(). I think the solution should reflect that. I.e. replace Lines 18 - 21 in the solution with:

  • Anthony

    For the note at the end, you could maybe add "debugging practice revisited" after some chapters since the basics have been covered. Would allow for some more intricate issues to solve and extra reinforcement for practicing good debugging habits.

  • Ethan Smith

    "The process of finding and removing errors from a programming is called debugging" i think you may have made a grammatical mistake there.

Leave a Comment

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