本机 C++ 中的表达式
调试器接受大多数 Microsoft 和 ANSI C/C++ 表达式。 调试器还提供 内部函数 和 上下文运算符 使计算表达式更安全、更为方便。 本主题还描述了对需要了解的下面的 C++ 表达式的限制:
不能将上下文运算符或大多数格式说明符用在代码中,或用于脚本或托管代码表达式。 它们特定于本机 C++ 表达式计算器。
本节内容
使用维护调试器 intrinisic 的功能的状态
使用指定上下文的运算符符号
本机 C++ 表达式的限制
访问控制
不明确的引用
匿名命名空间
构造函数、析构函数和转换
Inheritance
内联和编译器内部函数
数值常数
运算符函数
重载
优先级
符号格式
类型强制转换
使用维护调试器 intrinisic 的功能的状态
调试器内部函数为您提供了一种调用表达式中的某些 C/C++ 函数,而不更改应用程序的状态。
调试器内部函数:
确保是安全的:执行调试器内部函数不会损坏正在调试的进程。
在任何表达式允许,即使在副作用和函数中不允许的方案。
工作在正则函数调用是不可能的,例如调试小型转储的方案。
调试器内部函数还可以计算表达式更为方便。 例如,strncmp(str, “asd”) 比 str[0] == ‘a’ && str[1] == ‘s’ && str[2] == ‘d’可以轻松地在断点条件的写访问权。 )
区域 |
内部函数 |
---|---|
字符串长度 |
strlen,wcslen,strnlen,wcsnlen |
字符串比较 |
strcmp,wcscmp,stricmp,_stricmp,_strcmpi,wcsicmp,_wcscmpi,_wcsnicmp,strncmp,wcsncmp,strnicmp,wcsnicmp |
字符串搜索 |
strchr,wcschr,strstr,wcsstr |
Win32 |
GetLastError(),TlsGetValue() |
Windows 8 |
WindowsGetStringLen(),WindowsGetStringRawBuffer() 这些功能要求正在调试运行在 windows 8. 的过程。 调试转储文件从生成的 windows 设备还需要 8 的 Visual Studio 计算机运行 windows 8。 但是,因此,如果远程调试 windows 8 计算机上,Visual Studio 计算机上运行 windows 7。 |
杂项 |
__log2 返回指定的整数的记录 foundation 2,四舍五入到最近的下半部分整数。 |
使用指定上下文的运算符符号
上下文运算符是由本机调试器提供的附加运算符。 在调试本机代码时,可以使用上下文运算符限定断点位置、变量名或表达式。 上下文运算符对于某些目的很有用,如指定来自外部范围的、但被本地名称隐藏的名称。
语法
{,}],[模块表达式
模块 是模块的名称。 可以使用完整路径可消除具有相同名称的模块之间。
表达式 解析为有效的目标,如一个函数名、变量名或指针地址。模块的任何有效的 C++ 表达式。
大括号必须包含两个逗号和模块 (可执行文件或 DLL) 名称或完整路径。
例如,将断点设置为 EXAMPLE.dll 的 SomeFunction 功能:
{,,EXAMPLE.dll}SomeFunction
如果 模块 路径包括一个逗号、一个嵌入空格或一个大括号,必须在路径周围使用引号,以便上下文分析器可以相应地识别该字符串。 单引号被视为 Windows 文件名的一部分,因此必须使用双引号。 例如,
{,"a long, long, library name.dll", } g_Var
当表达式计算器遇到表达式中的符号时,它按下列顺序搜索该符号:
词法范围向外,从当前块开始(括在大括号中的一系列语句),然后从该封闭块继续向外。 当前块是包含当前位置(指令指针地址)的代码。
函数范围。 当前函数。
类范围,如果当前位置在 C++ 成员函数内。 类范围包含所有的基类。 表达式计算器使用正常域控制规则。
在当前模块的全局符号。
在当前程序的公共符号。
上下文运算符,可以指定搜索并跳过的起始模块当前位置。
本机 C++ 表达式的限制
当在调试器窗口中输入 C/C++ 表达式时,将受到下列常规限制:
访问控制
调试器可以访问所有的类成员而不用考虑访问控制。 可以检查任一类对象成员,包括基类和嵌入成员对象。
不明确的引用
如果调试器表达式引用不明确的成员名称,必须使用类名称来限定它。 例如,如果 CObject 是 CClass 实例,后者从 AClass 和 BClass 二者中继承了名为 expense 的成员函数,则 CObject.expense 是不明确的。 可以按如下方式化解多义性:
CObject.BClass::expense
若要化解多义性,表达式计算器应用关于成员名称的正常域控制规则。
匿名命名空间
本机 C++ 表达式计算器不支持匿名命名空间。 例如,假定您具有下列代码:
#include "stdafx.h"
namespace mars
{
namespace
{
int test = 0;
}
}
int main()
{
// Adding a watch on test does not work.
mars::test++;
return 0;
}
本示例中,唯一一种监视符号 test 的方式是使用修饰名:
(int*)?test@?A0xccd06570@mars@@3HA
构造函数、析构函数和转换
不能使用要求构造临时对象的表达式显式或隐式地为对象调用构造函数或析构函数。 例如,下列表达式显式调用构造函数并导致错误信息:
Date( 2, 3, 1985 )
如果转换的目标是类,则不能调用转换函数。 这种转换涉及到构造对象。 例如,如果 myFraction 是 CFraction 的实例,后者定义了转换函数运算符 FixedPoint,下列表达式导致错误:
(FixedPoint)myFraction
但是,如果转换的目标是内置类型,则可以调用转换函数。 如果 CFraction 定义转换函数 operator float,在调试器中以下表达式是合法的:
(float)myFraction
可以调用返回对象或声明局部对象的函数。
不能调用 new 或 delete 运算符。 下列表达式在调试器中不能运行:
new Date(2,3,1985)
Inheritance
当使用调试器显示具有虚拟基类的类对象时,为每个继承路径显示虚拟基类的成员,即使只存储了那些成员中的一个实例也是如此。
虚函数调用将被表达式计算器正确地处理。 例如,假定类 CEmployee 定义虚拟函数 computePay,该函数在从 CEmployee 继承的类中重新定义。 可以通过指向 CEmployee 的指针调用 computePay,并执行正确的函数:
empPtr->computePay()
可以将指向派生类对象的指针转换为指向基类对象的指针。 当继承是虚拟的时,可以将指针到基类对象到指针传递给派生类对象,但。
内联和编译器内部函数
除非该函数至少一次正常显示。功能,调试器表达式不能将编译器内部函数或内联函数。
数值常数
调试器表达式可以使用八进制、十六进制或十进制格式的整数常数。 默认情况下,调试器需要十进制常数。 此设置可以在**“调试”选项卡的“常规”**页上更改。
可以使用前缀或后缀符号表示另一基中的数字。 下表显示了可以使用的格式。
语法 |
示例(十进制 100) |
基数 |
---|---|---|
数字 |
100 或 64 |
十进制或十六进制,具体取决于当前的设置。 |
0 数字 |
0144 |
八进制(以 8 为基数) |
0n 数字 |
0n100 |
十进制(以 10 为基数) |
0x 数字 |
0x64 |
十六进制(以 16 为基数) |
数字 h |
64h |
十六进制(以 16 为基数) |
运算符函数
调试器表达式可以隐式或显式地调用类的运算符函数。 例如,假设 myFraction 和 yourFraction 都是定义 operator+ 的类的实例。 可以使用下列表达式显示这两个对象的和:
myFraction + yourFraction
如果将运算符函数定义为友元函数,则可以使用与调用成员函数相同的语法隐式调用它,或者显式调用它,如:
operator+( myFraction, yourFraction )
与一般函数一样,不能调用带有要求转换(涉及到对象构造)的参数的运算符函数。
调试器不支持同时具有常量和非常量版本的重载运算符。 带有 const 和非 const 版本的重载运算符常用在标准模板库中。
重载
如果存在精确匹配或者匹配不要求涉及对象构造的转换,则调试器表达式可以调用重载函数。 例如,如果函数 calc 将 CFraction 对象作为参数,并且 CFraction 类定义接受整数的单一参数构造函数,则下列表达式将导致错误:
calc( 23 )
即使存在将整数转换为 calc 期望的 CFraction 对象的合法转换,但这样的转换涉及到对象的创建,因此不受支持。
优先级
在调试器表达式中,C++ 范围运算符 (::) 比其在源代码中具有更低的优先级。 在 C++ 源代码中,该运算符具有最高的优先级。 在调试器中,其优先级介于基数与后缀运算符(->、++、--)和一元运算符(!、&、* 及其他)之间。
符号格式
如果符号所在的模块是用完全调试信息(/Zi 或 /ZI)编译的,则输入的包含符号的调试器表达式与源代码中使用的格式相同。 如果输入的表达式包含公共符号(即在库中或者在用 /Zd 编译的模块中可以找到的符号),则必须使用符号的修饰名(即在对象代码中使用的格式)。 有关更多信息,请参见 /Z7、/Zd、/Zi、/ZI(调试信息格式)。
使用 LINK /MAP 选项可以获得所有修饰和未修饰格式的名称列表。 有关更多信息,请参见 /MAP(生成映射文件)。
名称修饰是用于强制类型安全链接的机制。 这意味着只有拼写、大小写、调用约定和类型精确匹配的名称和引用才链接在一起。
用 C 调用约定声明(使用 _cdecl 关键字显式或隐式声明)的名称以下划线 (_) 开头。 例如,函数 main 可显示为 _main。 声明为 _fastcall 的名称以 @ 符号开头。
对于 C++,修饰名除了对调用约定进行编码外还对符号类型进行编码。 这种格式的名称会很长而且难以读取。 该名称以至少一个问号 (?) 开头。 对于 C++ 函数,修饰包括函数范围、函数参数的类型和函数返回类型。
类型强制转换
如果转换为类型,调试器必须已知该类型。 在程序中必须有该类型的另外一个对象。 不支持使用 typedef 语句创建的类型。