Partilhar via


Expressions in Native C++

The debugger accepts most Microsoft and ANSI C/C++ expressions. The debugger also provides intrinsic functions and context operators to make evaluating expressions safer and more convenient. This topic also describes the restrictions on C++ expressions that need to be aware of the following:

You cannot use the context operator or most format specifiers in code or with script or managed code expressions. They are specific to the native C++ expression evaluator.

In this section

Using debugger intrinisic functions to maintain state

Using context operators to specify a symbol

Restrictions on native C++ expressions

  • Access control

  • Ambiguous references

  • Anonymous namespaces

  • Constructors, destructors, and conversions

  • Inheritance

  • Inlined and compiler intrinsic functions

  • Numeric constants

  • Operator Functions

  • Overloading

  • Precedence

  • Symbol Formats

  • Type Casting

Using debugger intrinisic functions to maintain state

The debugger intrinsic functions give you a way to call certain C/C++ functions in expressions without changing the state of the application.

Debugger intrinsic functions:

  • Are guaranteed to be safe: executing a debugger intrinsic function will not corrupt the process that is being debugged.

  • Are allowed in all expressions , even in scenarios where side effects and function evaluation are not allowed.

  • Work in scenarios where the regular function calls are not possible, such as debugging a minidump.

Debugger intrinsic functions can also make evaluating expressions more convenient. For example, strncmp(str, “asd”) is much easier to write in a breakpoint condition than str[0] == ‘a’ && str[1] == ‘s’ && str[2] == ‘d’. )

Area

Intrinsic functions

String length

strlen, wcslen, strnlen, wcsnlen

String comparison

strcmp, wcscmp, stricmp, _stricmp, _strcmpi, wcsicmp, _wcscmpi, _wcsnicmp, strncmp, wcsncmp, strnicmp, wcsnicmp

String search

strchr, wcschr, strstr, wcsstr

Win32

GetLastError(), TlsGetValue()

Windows 8

WindowsGetStringLen(), WindowsGetStringRawBuffer()

These functions require the process that is being debugged to be running on Windows 8. Debugging dump files generated from a Windows 8 device also requires that the Visual Studio computer be running Windows 8. However, if you are debugging a Windows 8 device remotely, the Visual Studio computer can be running Windows 7.

Miscellaneous

__log2

Returns the log base 2 of a specified integer, rounded to the nearest lower integer.

Using context operators to specify a symbol

The context operator is an additional operator provided by the native debugger. When debugging native code, you can use the context operator to qualify a breakpoint location, variable name, or expression. The context operator is useful for purposes such as specifying a name from an outer scope that is otherwise hidden by a local name.

Syntax

{,,[module] } expression

  • module is the name of a module. You can use a full path to disambiguate between modules with the same name.

  • expression is any valid C++ expression that resolves to a valid target, such as a function name, variable name, or pointer address in module.

The braces must contain two commas and the module (executable or DLL) name or full path.

For example, to set a breakpoint at the SomeFunction function of EXAMPLE.dll:

{,,EXAMPLE.dll}SomeFunction

If the module path includes a comma, an embedded space, or a brace, you must use quotation marks around the path so that the context parser can properly recognize the string. Single quotation marks are considered part of a Windows file name, so you must use double quotation marks. For example,

{,"a long, long, library name.dll", } g_Var

When the expression evaluator encounters a symbol in an expression, it searches for the symbol in the following order:

  1. Lexical scope outward, starting with the current block, series of statements enclosed in braces, and continuing outward with the enclosing block. The current block is the code containing the current location, instruction pointer address.

  2. Function scope. The current function.

  3. Class scope, if the current location is inside a C++ member function. Class scope includes all base classes. The expression evaluator uses the normal dominance rules.

  4. Global symbols in the current module.

  5. Public symbols in the current program.

With the context operator, you specify the starting module of the search and bypass the current location.

Restrictions on native C++ expressions

When you enter a C/C++ expression in a debugger window, these general restrictions apply:

Access control

The debugger can access all class members regardless of access control. You can examine any class object member, including base classes and embedded member objects.

Ambiguous references

If a debugger expression refers to an ambiguous member name, you must use the class name to qualify it. For example, if CObject is an instance of CClass, which inherits member functions named expense from both AClass and BClass, CObject.expense is ambiguous. You can resolve the ambiguity like this:

CObject.BClass::expense

To resolve ambiguities, the expression evaluator applies normal dominance rules regarding member names.

Anonymous namespaces

The native C++ expression evaluator does not support anonymous namespaces. Suppose, for example, you have the following code:

#include "stdafx.h"

namespace mars 
{ 
    namespace
    {
        int test = 0; 
    } 

} 


int main() 
{ 
    // Adding a watch on test does not work. 
    mars::test++; 
    return 0; 
} 

The only way to watch the symbol test in this example is to use the decorated name:

(int*)?test@?A0xccd06570@mars@@3HA

Constructors, destructors, and conversions

You cannot call a constructor or destructor for an object, either explicitly or implicitly, using an expression that calls for construction of a temporary object. For example, the following expression explicitly calls a constructor and results in an error message:

Date( 2, 3, 1985 )

You cannot call a conversion function if the destination of the conversion is a class. Such a conversion involves the construction of an object. For example, if myFraction is an instance of CFraction, which defines the conversion function operator FixedPoint, the following expression results in an error:

(FixedPoint)myFraction

However, you can call a conversion function if the destination of the conversion is a built-in type. If CFraction defines a conversion function operator float, the following expression is legal in the debugger:

(float)myFraction

You can call functions that return an object or declare local objects.

You cannot call the new or delete operators. The following expression does not work in the debugger:

new Date(2,3,1985)

Inheritance

When you use the debugger to display a class object that has virtual base classes, the members of the virtual base class are displayed for each inheritance path, even though only one instance of those members is stored.

Virtual function calls are properly handled by the expression evaluator. For example, assume the class CEmployee defines a virtual function computePay, which is redefined in a class that inherits from CEmployee. You can call computePay through a pointer to CEmployee and have the proper function executed:

empPtr->computePay()

You can cast a pointer to a derived class object into a pointer to a base class object. You can cast a pointer to a base class object into a pointer to a derived class object, except when the inheritance is virtual.

Inlined and compiler intrinsic functions

A debugger expression cannot call an compiler intrinsic functions or inlined function unless the function appears at least once as a normal function.

Numeric constants

Debugger expressions can use integer constants in octal, hexadecimal, or decimal format. By default, the debugger expects decimal constants. This setting can be changed on the General page of the Debugging tab.

You can use prefix or suffix symbols to represent numbers in another base. The following table shows the forms you can use.

Syntax

Example (decimal 100)

Base

digits

100 or 64

Decimal or hexadecimal, depending on the current setting.

0digits

0144

Octal (base 8)

0ndigits

0n100

Decimal (base 10)

0xdigits

0x64

Hexadecimal (base 16)

digitsh

64h

Hexadecimal (base 16)

Operator Functions

A debugger expression can invoke operator functions for a class implicitly or explicitly. For example, suppose myFraction and yourFraction are instances of a class that defines operator+. You can display the sum of those two objects using this expression:

myFraction + yourFraction

If an operator function is defined as a friend, you can call it implicitly using the same syntax as for a member function, or you can invoke it explicitly, as follows:

operator+( myFraction, yourFraction )

Like ordinary functions, operator functions cannot be called with arguments that require a conversion involving object construction.

The debugger does not support overloaded operators with both const and non-const versions. Overloaded operators with const and non-const versions are used frequently in the Standard Template Library.

Overloading

A debugger expression can call overloaded functions if an exact match exists or if a match does not require a conversion involving object construction. For example, if the calc function takes a CFraction object as a parameter, and the CFraction class defines a single-argument constructor that accepts an integer, the following expression results in an error:

calc( 23 )

Even though a legal conversion exists to convert the integer into the CFraction object that calc expects, such a conversion involves the creation of an object and is not supported.

Precedence

In debugger expressions, the C++ scope operator (::) has lower precedence than it does in source code. In C++ source code, this operator has the highest precedence. In the debugger, its precedence falls between the base and postfix operators (->, ++, --) and the unary operators (!, &, *, and others).

Symbol Formats

You enter a debugger expression that contains symbols in the same form used in the source code, provided the symbols are in a module compiled with full debug information (/Zi or /ZI). If you enter an expression that contains public symbols, which are symbols found in libraries or in modules compiled with /Zd, you must use the decorated name of the symbol, the form used in the object code. For more information, see /Z7, /Zd, /Zi, /ZI (Debug Information Format).

You can get a listing of all names in their decorated and undecorated forms using the LINK /MAP option. For more information, see /MAP (Generate Mapfile).

Name decoration is the mechanism used to enforce type-safe linkage. This means that only the names and references with precisely matching spelling, case, calling convention, and type are linked together.

Names declared with the C calling convention, either implicitly or explicitly using the _cdecl keyword, begin with an underscore ( _ ). For example, the function main can be displayed as _main. Names declared as _fastcall begin with the @ symbol.

For C++, the decorated name encodes the type of the symbol in addition to the calling convention. This form of the name can be long and difficult to read. The name begins with at least one question mark (?). For C++ functions, the decoration includes the function scope, the types of the function parameters, and the function return type.

Type Casting

If you cast to a type, the type must be known to the debugger. You must have another object of that type in your program. Types created using typedef statements are not supported.