#include <iostream>
using namespace std;
class TestProperty{
public:int mp1;
int mp2;
int mp3;
};
void testProperty(){
cout<<&(((TestProperty*)0)->mp1)<<endl;
cout<<&(((TestProperty*)0)->mp2)<<endl;
cout<<&(((TestProperty*)0)->mp3)<<endl;
cout<<((TestProperty*)0)->mp1<<endl;
}
以上几个表达式能正常输出吗,如果能,输出的又是什么呢?
这是前三个表达式的输出,最后一个会造成程序的崩溃
编译器看到&(((TestProperty*)0)->mp1)这个表达式的时候,知道这句是为了获取mp1的地址,而此时,地址是知道的,所以就直接打印出来了,下面对应的汇编代码
73: cout<<&(((TestProperty*)0)->mp1);00401808 push 0
0040180A mov ecx,offset std::cout (0047eea0)
0040180F call @ILT+75(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401050)
74: cout<<&(((TestProperty*)0)->mp2);
00401814 push 4
00401816 mov ecx,offset std::cout (0047eea0)
0040181B call @ILT+75(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401050)
75: cout<<&(((TestProperty*)0)->mp3);
00401820 push 8
00401822 mov ecx,offset std::cout (0047eea0)
00401827 call @ILT+75(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401050)
其中的0 4 8 就是编译器已经算好的地址,准确的说,是偏移地址,相对于类首地址的偏移地址
那为何 最后一句会崩呢,下面是对应的汇编代码
76: cout<<((TestProperty*)0)->mp1;
0040182C mov eax,[00000000]
00401831 push eax
00401832 mov ecx,offset std::cout (0047eea0)
00401837 call @ILT+275(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401118)
此时编译器认为是想取得mp1的内容,而此时mp1还没分配好内存空间,于是在调用mov eax,[00000000]的时候,发生访问异常了。
把代码修改一下
class TestProperty{
public:
int mp1;
int mp2;
int mp3;
public:
void func1(){printf("hello TestProperty");}
};
void testProperty(){
(((TestProperty*)0)->func1());
printf("%d",((TestProperty*)0)->func1);//vc6可以这么写,但在vs2013中这么会报错
}
会正常输出吗?
为何第一句不会崩?因为方法早在类还没初始化的时候,已经在代码区生成了,它与类的构造没有关系,看对应的汇编代码
76: (((TestProperty*)0)->func1());
004013E8 xor ecx,ecx
004013EA call @ILT+0(TestProperty::func1) (00401005)
77: printf("%d\n",((TestProperty*)0)->func1);
004013EF push offset @ILT+0(TestProperty::func1) (00401005)
004013F4 push offset string "%d\n" (0043401c)
004013F9 call printf (00409260)
由于在调用func1的时候,应该在ecx传入调用者的地址,而这里为0,所以直接xor ecx,ecx对其清空了,然后正常的调用了func1,但如果func1中使用了this指针,那就
肯定非法了,因为此时this指针为null
对于printf("%d\n",((TestProperty*)0)->func1);就是打印函数的地址了,这个在vc6中可以正常运行,但在vs2013会报调用函数需要传参的错误,所以就用内联汇编来
代替了,如下:
char* myword = "%d\n";
__asm{
mov eax, offset TestProperty::func1
push eax
push dword ptr[myword]
call printf
add esp,8
}