条件断点
运行环境:
- Windows10
- Visual Studio 2019
- 编译器默认C++
Debug的条件断点的打开方式:断点右键-选择条件
使用这种方式可以减少用修改源码来断点。
条件断点是在断点处的约束触发条件,并且可以设置断点忽略次数,条件断点在多线程上也能使用,可以线程ID用来分离线程(只在指定的线程中断点)
断点优先级:线程>条件>忽略计数器
只有前面的条件满足了,忽略计数器才会自减。
比如说下面这段代码,其中设置了命中次数3,conditions是index==1
,那么debug会在第三个1中断,再点击运行不出触发后面1的中断,第一个0不会触发忽略计数器–。
我觉得触发条件可以等价下面的:
// 条件
bool condition(){
return (i == 3);
}
int count = 3;
while (count > 0){
if(condition()){
count--;
if(count == 0){
...// breakpoint
}
}
}
条件断点要注意1:
- 不能有副作用,有副作用等于无效判断
- 条件断点支持的函数:
strlen, wcslen, strnlen, wcsnlen, strcmp, wcscmp, _stricmp, _wcsicmp, strncmp, wcsncmp, _strnicmp, _wcsnicmp, strchr, wcschr, strstr, wcsstr
(某个网站上复制过来的,全不全不知道,但是看着够用) - 条件断点表达式能够直接访问
private
等受到限制的变量
判断字符串相等
错误案例
错误案例1
表达式为s == "abc"
得到:
错误案例2
看来条件断点不支持调用string
的 ==
重载,那么根据上面的注意事项第2点,改用strcmp
来实现:
第1种:strcmp(s, "abc") == 0
VS中断报错
这个肯定是不行的,就算放到代码里面也会有错。
第2种:strcmp(s.c_str(), "abc") == 0
VS显示有副作用,看来这种也不行,我觉得副作用可以ban掉,但是不妥所以没往这个方向研究,如果有人研究ban掉副作用,可以一起讨论一下。
错误案例3
条件表达式:s.compare("abc")==0
看VS的中断没有报错提示还以为没有问题,一运行又崩了。
可执行写法
既然上面的方式都不行,那就去只能查一下究竟要用什么方法才能不修改源码实现我想要的效果。
根据VS debug中断报错信息不存在“std::basic_string<char,std::char_traits<char>,std::allocator<char>>”到“const char *“适当的转换函数
查了许多网站后,找不到这类问题的解决方法(确实有解决的,但是那个问题是07年提出的,不是同一个版本的VS,它的解决方法在VS上显示有副作用),大概就是说明往这个方向深入是无解。
更换搜索关键词 vsdebug condition breakpoint strcmp
,最后在Stack Overflow上找到了解决方法2
(调试器中是不能直接判断字符串相等,采用的方法一般是逐字符判断,但是我不喜欢这样)
正确用法1
条件表达式 s.size() > 0 && strcmp(&s[0], "abc") == 0
说明:&s[0]
表示 char*(char)
,因为 []
的优先级比&
高,因此s[0]得到char的变量,取地址符&可以得到变量的地址,因此等价于&s[0]
可以获得非const
的s.c_str()
我推荐就用这个方法,可以不用考虑其他问题,在&&
之前加s.size()>0
就是避免s == ""
的情况。
正确用法2
条件表达式
(s.size() < 16 && strcmp(s._Mypair._Myval2._Bx._Buf,"123456789012345") == 0) || (s.size() >= 16 && strcmp(s._Mypair._Myval2._Bx._Ptr,"1234567890123456") == 0)
三目运算符也是可以的,建议先IDE里面写,太长容易写错。
(s.size() < 16) ? (strcmp(s._Mypair._Myval2._Bx._Buf, "123456789012345") == 0): (strcmp(s._Mypair._Myval2._Bx._Ptr, "1234567890123456") == 0);
分析string
这个实现方法跟string
内部实现有关系,虽然可以根据string.c_str()
猜出来上面这个怎么写,但是发现这些对像_Compressed_pair
这种STL类不了解,但是研究这个类不是debug的目的,这时候就该换个思路。
_Compressed_pair<_Alty, _Scary_val> _Mypair;
_NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ const _Elem* c_str() const noexcept {
return _Mypair._Myval2._Myptr();
}
首先c_str()
实现原理如上,由于条件断点的第三点条件断点表达式能够直接访问private等受到限制的变量
,因此我可以直接现在VS调试的监视里面查看string的数据,可以查到如下:
结果论的话,我们可以直接查看原始格式,表达式s
变成s,!
,这样就可以忽略任何数据类型视图自定义项,看到变量的真实结构。
这里需要说明std::string
原始结构3,下面的数据运行环境我在开头有写,也许其他系统的参数就不是这样:
_Mypair._Myval2._Mysize
:对应std::string.size()
,初始0_Mypair._Myval2._Myres
:对应std::string.capacity()
,初始15_Mypair._Myval2._Bx._Buf
:不是指针,它是std::string
用于保存字符串的内部小缓冲区,是一种缓冲区优化(不实际在堆上分配string),可以看到缓冲区的大小为16。当字符串长度小于16时,内存就指向_Buf区域_Mypair._Myval2._Bx._Ptr
:是指向堆缓冲区的指针,只有当字符串长度大于等于16时,内存才指向_Ptr区域_Mypair._Myval2._Bx
是_Buf
和_Ptr
的并集
字符串默认最大空间大小就是15,这跟缓冲区有关系,但是为什么我说缓冲区大小为16,而不是15,这是因为字符串结尾是一定是’\0’,因此传入第16个字符时,会导致’\0’跑到下标16上面,超出capacity
的15,接着触发1.5内存扩容。
综上所述,长度小于16用_Buf,否则_Ptr。
(s.size() < 16) ? (strcmp(s._Mypair._Myval2._Bx._Buf, "123456789012345") == 0): (strcmp(s._Mypair._Myval2._Bx._Ptr, "1234567890123456") == 0);
再深入就要看string的具体实现了,以及实现string的相关模板类原理,我找到一篇写得还可以的,找个时间再深入研究。
https://zhuanlan.zhihu.com/p/371669822
资料参考来源: