来宾帖子-表达式评估器

你好。我叫奥菲克·希伦 我写博客 主要是关于我遇到的各种VC++技巧。今天,我想明确地介绍一个调试特性,我们每天都使用它,但很少提到它的名字-本机 表达式计算 (以下简称EE)。

null

基础知识

每次你使用观察窗,幕后都会发生很多事情。无论何时键入变量名,都需要将该名称映射到已命名变量的内存地址和类型,然后根据其类型正确地显示该变量。相反,当您修改变量的内容时,需要将文本输入转换为正确的类型,并在正确的地址正确地更新内存。

那是什么 表达式计算器 . 这是一个令人印象深刻的,往往被忽视的技术,一旦熟悉它,你可以把它很好地利用,有时在令人惊讶的方式!

正如组件名称所示,它实际上能够解析和计算表达式:

图片[1]-来宾帖子-表达式评估器-yiteyi-C++库 s

这些表达式可以是赋值语句,也可以修改变量:

图片[2]-来宾帖子-表达式评估器-yiteyi-C++库

更重要的是,表达式可以包含函数调用!

图片[3]-来宾帖子-表达式评估器-yiteyi-C++库

EE存在于各种IDE上下文中—监视窗口、快速监视对话框、即时窗口、断点条件等等—但它在各种监视窗口中最明显(也最常用)。当熟练地使用此特性时,可以提高调试交互性到令人惊讶的水平,并且实际上更接近于完全C++解释的环境(尤其是在编辑和继续的同时)。但是,表达式求值和实际代码编译之间有几个重要的区别,您应该注意。

调用函数

什么叫能叫,什么叫不能叫

EE不能调用内联函数,因为它们并不是真正作为要调用的代码存在的。  从好的方面来说,EE对访问权限是盲目的,并且很乐意评估对私有方法或文件静态函数的调用。

副作用

表达式是在当前选定线程的上下文中进行计算的(即,其堆栈、寄存器和局部变量显示在相应的IDE窗口中的线程)。特别是,表达式是在被调试对象的地址空间中计算的,如果不小心的话,可能会产生意想不到的后果。例如,假设您正试图计算一个分配堆内存的表达式,但是另一个线程被冻结,持有堆锁。调试对象将处于未知状态,IDE应该采取措施防止自己挂起!  可能会出现许多类似的情况:由于所有调试对象线程都被冻结,各种资源可能处于不稳定状态,因此在处理它们时应小心。  这种思路可能是导致EE设计者明确禁止使用CRT和win32api的一个大子集的原因。被禁止的API的完整列表是一个实现细节,可能会在版本之间或更新中发生更改(在VS2010中确实扩展了很多)。

然而 ,

(1)    在实践中,所描述的副作用是非常罕见的,我多年来一直以交互方式处理debuggee状态,没有任何问题。

(2)    针对此列表的测试很浅,您可以通过使用自己的函数(提前,即在源代码本身)在列表中包装API来轻松绕过它们:

图片[4]-来宾帖子-表达式评估器-yiteyi-C++库

尽管如此,这些旁路是没有文件记录和不受支持的- 使用它们的风险自负!

上下文运算符

EE不包括完整的链接器,当您在主可执行文件之外调用函数时,EE可能需要帮助来解析调用。你可以用 上下文运算符 :

{,DllName.dll}函数名()

(也可以在模块名中省略“.dll”。)

图片[5]-来宾帖子-表达式评估器-yiteyi-C++库

一些粗糙的边缘

尽管EE解析和符号解析非常有用,但它可能永远不会像编译器那样健壮—有时一些解决方法是正确的。

符号有时需要通过获取地址和强制转换来“手动解析”:

图片[6]-来宾帖子-表达式评估器-yiteyi-C++库

枚举类型大部分是可识别的,但单个枚举数(枚举值)不是。对枚举类型的隐式强制转换也可能失败。幸运的是,您可以自己轻松地转换原始整数值:

图片[7]-来宾帖子-表达式评估器-yiteyi-C++库

如果符号驻留在名称空间中,则它们必须是完全限定的。如果函数是模板,那么模板类型必须是完全的(有时非常详细) 年) 明确规定。  你可能会遇到其他类似的行为。根据经验,当事情不像你预期的那样发展时,尽量明确。

应用

最后,这里有一些实际使用的例子——特别是对调试构建中内存问题的增强调查。

_crtcheck内存 基本上是遍历CRT堆,并通过检查CRT在分配块末尾插入的填充来检测越界写操作。现在,您可以确定损坏的根源,而无需在源代码处重复传播u CrtCheckMemory并重新编译。下面是腐败之前的评估:

图片[8]-来宾帖子-表达式评估器-yiteyi-C++库

两行之后(单击刷新后,重新计算):

图片[9]-来宾帖子-表达式评估器-yiteyi-C++库

如果在源位置定义了一个u CrtMemState插槽,则可以使用 CRT提供的许多工具 . 首先填充:

图片[10]-来宾帖子-表达式评估器-yiteyi-C++库

然后探索它的内容——CRT API最终调用OutputDebugStringA,它仍然转储到输出窗口:

图片[11]-来宾帖子-表达式评估器-yiteyi-C++库

图片[12]-来宾帖子-表达式评估器-yiteyi-C++库

您还可以在源位置保留几个u CrtMemoryState,在不同的位置填充它们,并使用u CrtMemDifference对它们进行区分。 以此类推——你明白了。

底线

希望这已经足够作为这个不受重视的调试特性的介绍了。我很想听到(通过评论, 我的博客 或者仅仅是在gmail上的一次咨询)是否所有这些都适合你,以及你将要去的其他酷的方向。

我深深地感谢埃里克·巴塔利奥和詹姆斯·麦克内利斯,感谢他们让这篇文章成为现实并不断改进。

干杯,

-奥菲克

谢谢你的文章,奥菲克。 读者们,如果你对一篇文章有什么想法 这个 Visual C++/C++社区,Ping Me@ ebattali@microsoft.com . 我鼓励你 即使你认为你 不会写,题目可能会 不合适,或 你还有别的预约吗!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享