#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 00040180A 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 400401816 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 800401822 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 eax00401832 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,ecx004013EA 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::func1push eaxpush dword ptr[myword]call printfaddesp,8}
收敛自己的脾气,偶尔要刻意沉默,