#include<iostream> using namespace std; struct S { int a; int *p; }; S s; int main() { int *temp=&s.a; temp[0]=10; temp[1]=12; s.p=temp; s.p[1]=20; s.p[0]=120;//报错 return 0; }
请问最后s.p[0]=120;为什么会报错?
原因如下:
住要是由于把temp赋值给了s.p,即把结构体的第一个变量的地址赋值给第二个变量的地址,又temp的内容(内存地址)=&s.a=s.p=&s.p[0],那么s.p[1]的地址也就是s.p的地址了(int类型是4个字节的,所以s.p[1]地址=&s.p[0]+4,s.p地址=temp(也就是&s.a)+4,很恐怖,数组的第二个变量的地址等于了数组首地址的地址了,换句话说也就是s.p[1]的内容和s.p的内容(存放的是数组首地址&s.p[0])是相等的,在意义上一个是地址一个是变量内容,竟然用同一个内存地址)。。。
经过s.p[1]=20赋值后; s.p内容(也就是s.p[0]的地址)就成了20,而s.p是等于数组首地址s.p[0]的,那么就是s.p[0]的地址变成了0x14了。
那么最后一句s.p[0]=120的意思就是把120写入到0x14这个地址指向的内容中去(这些地址的内容一般是RAM或者OS底层用了),当然会报错了。
我这边各变量地址及内容如下:
temp=00477738
&temp=0012FF7C
s.p=00477738
&s.p=0047773C
s.p[0]=10//因为temp[0]=10
&s.p[0]=00477738
s.p[1]=4683576
&s.p[1]=0047773C
可见temp=&s.p[0]=s.p;&s.p=&s.p[1]
反汇编如下:
12: int *temp=&s.a; 00401048 mov dword ptr [ebp-4],offset s (00432e08) 13: temp[0]=10; 0040104F mov eax,dword ptr [ebp-4] 00401052 mov dword ptr [eax],0Ah 14: temp[1]=12; 00401058 mov ecx,dword ptr [ebp-4] 0040105B mov dword ptr [ecx+4],0Ch 15: s.p=temp; 00401062 mov edx,dword ptr [ebp-4] 00401065 mov dword ptr [s+4 (00432e0c)],edx 16: s.p[1]=20; 0040106B mov eax,[s+4 (00432e0c)] 00401070 mov dword ptr [eax+4],14h 17: s.p[0]=120; 00401077 mov ecx,dword ptr [s+4 (00432e0c)] 0040107D mov dword ptr [ecx],78h 18: 19: return 0; 00401083 xor eax,eax 20: }
00432E08 0A 00 00 00 08 2E 43 ---------------------00432E0C这个地址是s.p的地址(&s.p),s.p内容是(432e08),它(等于s.p[0]的地址)指向的内容是0A(十进制10)
00432E0F 00 00 00 00 00 00 00
00432E16 00 00 24 E0 42 00 20
执行完上面16步之后,s.p[1]=20;变成向s.p也就是&s.p[0]的内容432e08写20(因为s.p[1]的地址是等于s.p的地址的,给s.p[1]赋值就相当于给s.p的内容赋值,而s.p中存放的是s.p[0]的地址),执行完16步之后,内存状态如下:
00432E08 0A 00 00 00 14 00 00 ----------------------08 2E 43 变成了14 00 00(注意变量在内存中的存放方式,低位在前,高位在后)
00432E0F 00 00 00 00 00 00 00
00432E16 00 00 24 E0 42 00 20
执行到17步第一个mov ecx,dword ptr [s+4 (00432e0c)]后,ECX寄存器的值变成了00000014,最后一步编译器报错,提示该内存地址不能READ。
--------------------
希望大家也掌握这种反汇编分析方法来分析程序,这样分析的更透彻(因为能看到各个寄存器内存地址的变动)。