C++ Builder Tutorials

C++ Tutorial: Debugging

C++ Builder includes an integrated debugger, that lets you find both run-time errors and logic errors in your application. Using the debugger, you can step through code, set breakpoints and watches, and inspect and modify program values.


Quick way to inspect variables

For starters, let's have look at inspecting what's going on, without using the debugger.

Let's suppose that you have the following event handler:

void __fastcall TForm1::Button1Click(TObject *Sender)
{ 
  String S;
  S = Edit1->Text;
  S.Delete(1, 2);
  // and so on...
}

Quickly inspect a variable with a ShowMessage dialog box:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  String S;
  S = Edit1->Text;
  ShowMessage("Edit1 text: " + S); 
  // and so on...
}

Or add a TMemo to the form that you're working on. Next, add a few lines like this:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  String S;
  S = Edit1->Text;
  Memo1->Lines->Add("Edit1 text: " + S); 
  Memo1->Lines->Add("Length: " + IntToStr(S.Length())); 
  S.Delete(1, 2);
  Memo1->Lines->Add("After delete: " + S); 
  Memo1->Lines->Add("Length: " + IntToStr(S.Length())); 
  // and so on...
}

Debug Inspector

The Debug Inspector lets you examine various data types such as arrays, classes, constants, functions, and more. It is only available when the process is paused in the debugger.

To open the Debug Inspector:

  1. Place the cursor in the code editor on the line that you want to examine.
  2. In the menu select Run / Run to cursor (or press function key F4). Your application will start running and it stops on that line.
  3. In the menu select Run / Evaluate/Modify...
  4. Type an expression in the Expression edit box and click one of the following:

    • Evaluate: evaluates the expression and displays its value in the Result edit box
    • Watch: adds the expression to a "watch list", that can be consulted at run time.
    • New value: assigns a new value to the item specified in the Expression edit box. Enter a new value for the item if you want to change its value.

To inspect variables in Debug mode, right-click a variable in the code and select Debug / Evaluate/Modify.

When the program is paused, select Run / Trace Into (or press F7) to step through the code line by line.
Press F9 to continue at normal speed.


Breakpoints

A breakpoint pauses the program at a specified location. You can set breakpoints in the Code Editor before or during a debugging session.

To set or reset a breakpoint, right click on a line of code and select Debug / Toggle Breakpoint (or click in a line and press F5).

Breakpoint Icons

During a debugging session, the lines of code where you can set a breakpoint are marked with a blue dot in the left gutter. The following icons are used:

Icon Description of breakpoint
Valid and enabled. The debugger is inactive.
Valid and enabled. The debugger is active.
Invalid and enabled. Breakpoint is set at an invalid location, such as a comment, a blank line, or an invalid declaration.
Valid and disabled. The debugger is inactive.
Valid and disabled. The debugger is active.
Invalid and disabled. Breakpoint is set at an invalid location.


Mysterious error messages

When the IDE refuses to compile your program, it gives you an error message. But sometimes these messages can be quite cryptic. Some examples:

Error message Meaning / examples
'x' is not a member of 'z' Assuming that S is declared as a String, the code i = S.length(); causes
'length' is not a member of 'UnicodeString'
Meaning: 'length' is not recognized as a method of a String object.
Solution: Length must start with a capital L.
Function call missing ... Memo1->Lines->Add(S; causes
Function call missing )
Solution: add a right bracket: Memo1->Lines->Add(S);
Statement missing ; The following code causes an error:
if (S.IsEmpty())
Memo1->Lines->Add("Empty")
else

The debugger indicates the line with "else" as the offending one...
Solution: the previous line is missing a ;     it must be:
Memo1->Lines->Add("Empty");
Misplaced break Your code contains a break statement outside a switch or looping construct. Only use break statements inside of switch statements or loops.
Misplaced continue Your code contains a continue statement outside a looping construct.
Misplaced decimal point Your code contains a decimal point in a floating-point constant as part of the exponent.
Declaration terminated incorrectly A declaration has an extra or incorrect termination symbol, such as a semicolon placed after a function body.
Misplaced else Your code contains an else statement without a matching if statement. Possible causes:
  • An extra "else" statement
  • An extra semicolon
  • Missing braces
  • Syntax error in a previous "if" statement
Not an allowed type Your code declared some sort of forbidden type, for example, a function returning a function or array.
   
   
   
   

Exceptions: try / catch

Runtime errors are more difficult to debug than design time errors. You should try to intercept runtime errors as much as possible, this avoiding the user to think that you are incompetent.

When a runtime error occurs in an application, such as attempting to divide by zero, a so-called exception is "raised".

To handle these exceptions yourself, instead of letting the program simply crash, precede suspected blocks of code by the keyword try. Following the try keyword comes a block of code that the program will test for exceptions. If an exception occurs, the program flow is interrupted. The sequence of steps taken is as follows:

  • The program searches for a matching exception handler
  • If a handler is found, program control is tranferred to the handler
  • If no matching handler is found, the program will show a generic error message and it will terminate

The keyword catch marks an exception handling block of code. Syntax:

     catch (exception_declaration) group_of_statements

The exception_declaration can be:

  • a simple or structured type, such as (int) or (int x)
  • a pointer to a class, such as (EConvertError &E) for catching conversion errors
  • the notation (...) to catch all types of exceptions
Note: EConvertError occurs when an invalid attempt is tried to convert an integer, float, date, or time to a string, or vice versa, to convert a string to one of these other types. This error also occurs when an invalid argument is passed to a formatting routine, such as:

Date1 = StrToDateTime("99/99/2018");


Examples of try / catch:

try {
  X = StrToFloat(Edit1->Text);
} 
catch (int) {
  ShowMessage("An invalid number was entered");
}
//---------------------------------------------
try {
  X = StrToFloat(Edit1->Text);
} 
catch (...) {
  ShowMessage("An invalid number was entered");
}
//-------------------------------------
try {
  X = StrToFloat(Edit1->Text);
} 
catch (EConvertError &E) {
  ShowMessage("An invalid number was entered");
}