visual studio中使用printf打印迭代器内容时与cout的差别
发布日期:2021-10-07 04:43:13 浏览次数:7 分类:技术文章

本文共 2635 字,大约阅读时间需要 8 分钟。

迭代器是c++中用于遍历容器中的元素的数据类型,但是今天在使用vs2015进行调试的时候,发现对于c++的容器的实现,vs 2015与vc++ 6.0有很大的差异,应该是使用vs进行编译时的一个漏洞吧。

问题起源

关于下面的代码,在vs 2015和vc++ 6.0两个平台上的运行结果大不相同。

#include
#include
using namespace std;void main() { string start = "ASD"; string::iterator a = start.begin(); printf("%x,%x,%x\n", a,*a,*a); getchar();}

在以往使用迭代器的时候,笔者总是使用cout进行输出,但是今天由于一些格式上的原因使用了printf对内容进行输出,但是输出结果与预期差异过大。按照笔者分析,使用printf进行输出,三个内容应该依次为 迭代器指向的地址,’A’的十六进制,’A’的十六进制。但是最后的输出结果却如图所示:

vs输出结果

vc++的输出结果和预期的一样,但是为何同样的代码在两个平台上的输出结果不一样呢,这个问题引起了笔者的深思。

测试另一段代码

起初,我以为在c++中,由于容器的结构的原因,printf不能对容器这个对象进行解释,所以不能用printf对容器进行输出*a,因此,我尝试了如下代码,测试能否使用printf输出*a。

#include
#include
using namespace std;void main() { string start = "ASD"; string::iterator a = start.begin(); printf("%x,%x\n",*a,*a); getchar();}

但是输出结果却如我所料,输出了两个0x41,因此,可以断定,使用printf可以正确的输出迭代器指向的内容。但是为什么会出现上述的问题呢?

反汇编查看代码

为了搞清楚问题的根源,我们对目标程序的反汇编代码进行了分析,反汇编代码如下所示,为了方便,直接将注释写在下面的代码片段:

lea         ecx,[a]  call        std::_String_iterator
> >::operator* movsx eax,byte ptr [eax] push eax //该代码片段为将迭代器的地址放到ecx寄存器中,然后调用迭代器中重载的运算*,//取出迭代器当前指向的字符,并将地址存储在eax中,然后使用movsx将字符存入eax并压栈。lea ecx,[a] call std::_String_iterator
> >::operator* movsx ecx,byte ptr [eax] push ecx //该代码片段同上sub esp,0Ch //提前提升堆栈空间,用于存储迭代器mov edx,esp //将esp存储到edx,用于之后将多个内容移动到堆栈的时候进行寻址mov eax,dword ptr [a] //取得迭代器a的起始偏移,然后在后方依次将a的内 //容存储到预留的堆栈空间,生成一个a的副本mov dword ptr [edx],eax mov ecx,dword ptr [ebp-40h] mov dword ptr [edx+4],ecx mov eax,dword ptr [ebp-3Ch] mov dword ptr [edx+8],eax //迭代器a的副本生成结束push 41CC1Ch //0x41cc1c指向printf的格式化参数列表,即"%x,%x,%x\n"call _printf (04115F5h) add esp,18h

通过上述汇编代码可以看出,在printf执行前堆栈的状态如下所示:

0x0019FDD4  1c cc 41 00  .?A.        格式化字符串地址0x0019FDD8  e0 1c 5c 00  ?.\.        迭代器a的副本偏移两个字节0x0019FDDC  00 00 00 00  ....        迭代器a的副本偏移一个字节0x0019FDE0  d8 fe 19 00  ??..        迭代器a的副本偏移0个字节,三个字节组成迭代器a的副本 0x0019FDE4  41 00 00 00  A...        第一个*a 0x0019FDE8  41 00 00 00              第二个*a

但是经常使用printf都知道,printf中对参数进行定位都是使用ebp+xxx进行定位的,而且每个参数都是一个机器长度。只是解释方式用格式化字符串进行判定解释为指针还是对应的数,因此,可以判定,此处printf认为传入三个参数分别为迭代器副本的三个字节,因此出现了上述的问题。

猜想代码测试

基于上面的猜想,在printf看来,传入的参数数量应该是五个,所以,笔者写出了如下的代码用于验证猜想:

void main() {    string start = "ASD";    string::iterator a = start.begin();    printf("%x,%x,%x,%x,%x\n",a,*a,*a);    getchar();}
64fc88,0,19fed8,41,41

输出结果果然与预期的一样。问题到此算是搞清楚了。

使用vc++ 6.0进行编译

在vs中存在该问题,所以笔者也想测试下载vc++ 6.0上的表现。在vc++ 6.0进行编译的时候,输出的结果均为想要得到的结果。当该代码运行在vc++ 6.0中的时候,运行结果如下图所示:

这里写图片描述
可见vc++ 6.0中并未出现和vs中一样的问题,这个应该算是vs在编译的时候存在的一个问题吧。但是为什么会出现这种问题呢?两者有什么在对迭代器的实现上有什么差异呢?请见下回分解。

转载地址:https://blog.csdn.net/meanong/article/details/80489247 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:QQ快速登录协议的分析与漏洞利用
下一篇:ShellCode自定位的几种实现方法

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月11日 00时51分43秒