您的位置:首页 > 运维架构

&(((AClass*)0)->aProperty) 这样的表达式会报错吗?

2015-06-14 11:44 323 查看
#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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: