首页
社区
课程
招聘
[旧帖] [原创]学习C++时遇到的一个小问题&&解决 0.00雪花
发表于: 2009-12-13 15:22 3409

[旧帖] [原创]学习C++时遇到的一个小问题&&解决 0.00雪花

2009-12-13 15:22
3409

学习C++时遇到的一个小问题&&解决
一、疑惑
 
以上的代码运行之后输入
first   255   second   1023
为什么输出是second|255,而不是second|1023或者first|1023呢?
二、通过汇编代码查看
在VC++6.0中设置断点,按F5运行。
 
从上面的代码可以发现,编译器是按从右到左进栈的。这应该就是_cdecl调用方式的特点。
00401874:调用了a.get_score(a),在命令提示符界面上输入小于10个字符串+int型数据。
然后00401879~0040187D:是进栈的操作,用于最后的输出’|’和socre的值
进制顺序为:a.socre的值-->’|’的地址
00401889:第二次调用a.get_score(a),在命令提示符上输入小于10个字符串+int型数据。
0040188E:函数返回值EAX是a.name的地址,进栈,用于输出name的字符串
00401894:输出的是第二次输入的name
0040189D:输出的是’|’
004018A7:输出的是第一次输入的socre
004018AE:输出的是endl
输出为:second|255
        
总结:原来编译器这样子做的,关键的地方就是编译器在第一次调用a.get_score(a)之后就对输出进行了处理, socre是直接采用int值进栈的,所以导致了在第二次调用a.get_score(a)之后,输出的socre是第一次输入的,因为之前进栈的是立即数,不是地址。
三、如果socre的类型不是int呢?
①  成员变量修改为:
char name[10];  
    char socre[10];
 
00401379:用于输出socre,此时进栈的不是立即数,而是地址了
②成员变量修改为:
    int name;  
int socre;
输入:12  34  56  78
输出:56|34
 
00401890:输出name,进栈的是立即数
所以,字符串输出用到地址,数值输出用到数值本身。

四、如果编译器不是VC++6.0,而是采用g++在命令行里编译
输入:first   255   second   1023
输出:second|1023
显然跟用cl.exe编译不同,为什么?
不知道该怎么直接查看汇编代码,所以就用OD来查找问题
通过超级字符串查找’|’,找到了输出的地方,然后在代码的周围寻找关键的地方,设置断点
截图+注释如下:
 
同样是_cdecl调用方式。跟之前VC的明显不同是它把两次a.get_score(a)放在一起执行了,把调用的函数都完成了之后再来处理输出。在004013CD处保存了a的地址,而不是a.socre的值,然后在输出socre时(00401403行)用上了。在输出时也跟VC不同,采用的是地址,而不是立即数,所以输出的是第二次赋给a.socre的值。跟采用VC的不同应该就是编译器处理的不同。
六、g++对以函数为参数的cout输出是怎么处理的
增加函数int tdt (),修改cout行。如下
 
OD截图如下:
 
原来对cout<<a.get_score(a).name<<"|"<<a.get_score(a).socre<<"|"<<tdt()<<endl;的处理真的就是首先处理cout行中的函数参数,然后再处理cout。
int tdt()返回的是值,所以后面输出时cout参数进栈的也是值
五、总结
VC++6.0对于cout<<a.get_score(a).name<<"|"<<a.get_score(a).socre<<endl;的处理,关键的是它用的是_cdecl调用方式,而且是参数逐个处理。还有,对于数值,它进栈的是数值,对于字符串,它进栈的是地址,这跟cout的参数里的函数返回的类型无关,而跟它最终要输出的值的类型有关。第一次在命令命令提示符下面输入的name和socre其实是源代码行中的第二个a.get_score(a)。
而采用g++的方式编译,它是先处理cout中的参数,把每个函数都处理完之后再处理cout,所以在输出时,传递cout的参数用的是地址。
对于普通的cout<<a.name<<"|"<<a.socre<<endl;两种编译就没什么差别了。
g++的方式编译,cout<<XXX().b<<XXX().a<<endl;在输出时函数cout的参数进栈的类型跟XXX()返回值同种类型。例如:
cout<<a.get_score(a).name<<"|"<<a.get_score(a).socre<<"|"<<tdt()<<endl;
1、如果tdt()返回的是int类型,cout参数进栈的是实际值
2、如果tdt()返回的是引用,cout参数进栈的是地址。
觉得用cl和g++编译导致输出的不同,根本原因就是cout参数是传递值还是传递地址的不同。

PS:最近才开始学习C++,学习的时候发现了这个小小的问题,最终把问题解决了,知道了到底是怎么回事。先是对_cdecl的调用方式理解不透彻,通过在VC++6.0中看汇编代码,明白是_cdecl方式+cout的参数传递的问题;之后,用g++编译,又出现了问题,同样的_cdecl方式,输出却有差别,通过OD查看生成的exe文件,发现,g++对cout输出的处理跟VC的有差异。写完了总结,发现好像很简单,而且写得有点混乱。也许结果很简单,最重要的是对问题锲而不舍的求知过程。。由于对编译器学习得很少,C++也是刚开始学,所以有一些理解和描述可能有错误。

                                                                     09.12.13


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (27)
雪    币: 417
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
嘿嘿
好好学习天天向上,
2009-12-13 16:11
0
雪    币: 31
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
又跟着LZ学习了下
2009-12-13 17:02
0
雪    币: 81
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
看不懂 顶下
2009-12-19 17:57
0
雪    币: 30
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
很好啊~学习了~谢谢分享~
2009-12-19 20:47
0
雪    币: 28
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
好好学习一下啊~
2009-12-30 23:09
0
雪    币: 47
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
看不懂.. 学习吧哎
2009-12-31 08:18
0
雪    币: 30
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
在淘宝上订了本C++,好好学习天天向上额
2010-10-8 21:29
0
雪    币: 83
活跃值: (43)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
这个问题完全可以用C++语法来解释,用OD来调试有点杀鸡用牛刀的感觉,不过楼主的研究精神不错。建议将C++基础打好,不然遇到复杂的数据结构可不容易调试出来噢。

cout<<a.get_score(a).name<<"|"<<a.get_score(a).socre<<"|"<<endl;
上述代码等价于:
int score = a.get_score(a).socre;        //first + 255
char *name = a.get_score(a).name;  //second + 1023

cout<<name<<"|"<<score<<"|"<<endl;

因此结果当然是second,255了。
不过还是需要注意的是,红色的两行代码在不同的编译器下面执行的先后顺序是不一样的。好像MSVC的编译器和现在的结果一致。
2010-10-8 22:47
0
雪    币: 255
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
学习了···
2010-10-8 23:02
0
雪    币: 29
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
喜欢这种小问题 具体 很实用 学习了
2010-10-8 23:08
0
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
学习了。。。。。。。。
2010-10-9 08:21
0
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
我的钱涨的好慢啊 一天才一个
2010-10-9 08:22
0
雪    币: 34
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
看不懂,好好学习天天向上
2010-10-9 08:45
0
雪    币: 22
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
c++ 还是相对好学的~
2010-10-9 09:23
0
雪    币: 25
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
向你学习:)
2010-10-9 09:38
0
雪    币: 9
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
你看我还在这里混,就知道我什么都不懂了,谢谢,路过!
2010-10-9 09:59
0
雪    币: 35
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
跟着学习了!不过我学的还没有lz学的那么深奥!
2010-10-9 14:45
0
雪    币: 678
活跃值: (101)
能力值: ( LV2,RANK:150 )
在线值:
发帖
回帖
粉丝
19
VC编译器跟GCC/G++在对于结构体的内存对齐方面并不一样,VC一般都是进行内存对齐的,但是GCC/G++都没有。
2011-3-21 20:43
0
雪    币: 42
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
其实就是一次覆盖而已,当你第二次传入参数的时候 因为是引用 所以实参和形参是同地址同内容的
第一次写入 a.GetSocre(a).m_szName  
             == 》 this内存地址为:0x0012ff70 数据为 first(10个字节的内容) + 255(int)
             test 的内存地址也是   0x0012ff70 数据为 first(10个字节的内容) + 255(int)
第二次写入 a.GetSocre(a).m_nSocre
           == 》 this内存地址为:0x0012ff70 数据为 second(都写在这个地址0x0012ff70  内容被覆盖了) + 255(int)
        test 的内存地址也是   0x0012ff70 数据为 second(10个字节的内容) + 255(int)

注:当你写入超过10个字节的时候 连255也会被覆盖掉 不信的话LZ可以试下的
   但是只会显示10个字节和一个int的数据
2011-3-22 00:11
0
雪    币: 60
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
现在看不懂,先收藏,以后看
2011-3-22 11:37
0
雪    币: 23
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
完全有压力,看不懂,不过佩服楼主啊
2011-3-22 11:53
0
雪    币: 201
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
我跟了一下 发现原因是在第一次call get_score后面push过一次数值(push ecx,也就是0x000000ff这个值),在我机子上是存放于0012ff1c地址处,而在后面的call @ILT+350处的输出数值调用中用的是这里的值
004018A5   mov         ecx,eax
004018A7   call        @ILT+350(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401163)
继续跟进去是_Myt& operator<<(int _X)这个函数,这里传的是0x12ff1c这个地址中的值,因为[ebp+8]=[0x12ff14+8]=0x000000ff
想请问一下这个函数是怎么传参的 ,没有看到栈传参的push,而如果是寄存器传参也不对啊 因为ecx中存放的和0x12ff1c没一点关系。
2011-3-23 02:19
0
雪    币: 201
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
求楼主和诸位帮小弟解惑,谢谢了
2011-3-23 02:21
0
雪    币: 201
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
是存在覆盖的问题 但是超过10个字节并没有覆盖255 你试过吗?试过的话你给个输入用例。
我输入的用例是:asdsdsfdsgdg 255 awaeererwere 1023
输出是 :       awaeererwere  ??|255 还没报错,之后再长点就程序报错了
2011-3-23 02:29
0
游客
登录 | 注册 方可回帖
返回
//