【反汇编分析】下标寻址和指针寻址
来源:互联网 发布:mac系统安装 编辑:程序博客网 时间:2024/06/10 03:22
本节研究数组下标寻址和指针寻址的相关问题;
基本知识
数组
(1)数组名的值是一个指针常量;如int a[10],a为一个指向int的指针常量;注意不能修改常量的值;请注意a和&a虽然地址值是一样的,但是寻址的地址空间是不一样的;比如a+1是指向数组第2个变量,而&a+1是越过整个数组的指向;具体如下:
int a[5]; int *b = a; int (*c)[5] = &a; int d[4][5]; int (*e)[5] = d;
说明几点:
(1.1)b指向a的首元素;c指向数组a的整个地址空间(c+1将会跳过整个数组a的地址空间),e指向d[0]的地址空间,因此e+1将会指向d[1]的地址空间;
示意图如下:
指针
(1)指针其实就是存放的一个地址;而指向指针的指针也就是存放一个地址的地址;如int *b=a;b中存放的就是变量a的地址,而int** c=&b;c中存放的是变量b的地址;
代码片段如下:
int a = 1; int *b = &a; int **c = &b; cout << &a << endl; cout << b << endl; cout << c << endl;输出如下:
0xbf9449f80xbf9449f80xbf9449f4
示意图如下:
交换两个变量
(1)使用中间内存temp,temp通常是寄存器(那么交换两个变量,涉及到两次内存读和两次内存写),但是也有可能是栈上内存(那么交换两个变量,涉及到3次内存读和3次内存写),但是栈已经开在那了,不用也浪费;
(2)不使用中间内存;针对指针寻址,如果使用异或的方法,每一次异或操作,都会涉及到两次读和一次写(编译器不优化),如*begin ^= *end,因此总共就有6次内存读和3次内存写,3次异或;编译器优化的话,也需要两次内存读,3次内存写和3次异或操作;
数组寻址和指针寻址
栈中字符串
代码片段如下
int main(){ char a[] = "hello"; char* b = a; printf("%c\n", a[0]); printf("%c\n", *b); return 0;}
反汇编部分代码如下
80485e6:55 push %ebp 80485e7:89 e5 mov %esp,%ebp 80485e9:51 push %ecx 80485ea:83 ec 14 sub $0x14,%esp 80485ed:c7 45 ee 68 65 6c 6c movl $0x6c6c6568,-0x12(%ebp) #存放lleh(68656c6c)到ebp指定的地址ebp-0x12 80485f4:66 c7 45 f2 6f 00 movw $0x6f,-0xe(%ebp) #存放o(6f)到ebp指定的地址ebp-0xe 80485fa:8d 45 ee lea -0x12(%ebp),%eax #取地址到eax 80485fd:89 45 f4 mov %eax,-0xc(%ebp) #存放eax到ebp-oxc,其实就是b 8048600:0f b6 45 ee movzbl -0x12(%ebp),%eax #直接从数组中获得a[0] 8048604:0f be c0 movsbl %al,%eax 8048607:83 ec 08 sub $0x8,%esp 804860a:50 push %eax 804860b:68 68 87 04 08 push $0x8048768 8048610:e8 83 fe ff ff call 8048498 <printf@plt> 8048615:83 c4 10 add $0x10,%esp 8048618:8b 45 f4 mov -0xc(%ebp),%eax #取地址b到eax,一次寻址,获得b 804861b:0f b6 00 movzbl (%eax),%eax #取eax地址的内容到eax(零扩展),二次寻址,获得*b 804861e:0f be c0 movsbl %al,%eax #取eax内容的低16位到eax(符号扩展) 8048621:83 ec 08 sub $0x8,%esp 8048624:50 push %eax 8048625:68 68 87 04 08 push $0x8048768 804862a:e8 69 fe ff ff call 8048498 <printf@plt>
说明几点:
(1)由反汇编可以得出,数组寻址只需要经过一次寻址就可以获得a[0]的内容,通过movzbl -0x12(%ebp),%eax来获得a[0]的;而指针寻址需要两次寻址,mov -0xc(%ebp),%eax首先获得b本身的值,然后通过movzbl (%eax),%eax 获得*b的内容;
常量区中字符串
代码片段如下
int main(){ char* a = "hello"; char* b = a; printf("%c\n", a[0]); printf("%c\n", *b); return 0;}反汇编分析如下
80485ed:c7 45 f4 68 87 04 08 movl $0x8048768,-0xc(%ebp) #将"hello"字符串的地址放入到ebp-0xc(其实也就是a) 80485f4:8b 45 f4 mov -0xc(%ebp),%eax #将地址a赋值给eax 80485f7:89 45 f0 mov %eax,-0x10(%ebp) #将eax赋值给b 80485fa:8b 45 f4 mov -0xc(%ebp),%eax #取地址a 80485fd:0f b6 00 movzbl (%eax),%eax #取a的内容a[0] 8048600:0f be c0 movsbl %al,%eax 8048603:83 ec 08 sub $0x8,%esp 8048606:50 push %eax 8048607:68 6e 87 04 08 push $0x804876e 804860c:e8 87 fe ff ff call 8048498 <printf@plt> 8048611:83 c4 10 add $0x10,%esp 8048614:8b 45 f0 mov -0x10(%ebp),%eax #取地址b 8048617:0f b6 00 movzbl (%eax),%eax #取b的内容*b 804861a:0f be c0 movsbl %al,%eax 804861d:83 ec 08 sub $0x8,%esp 8048620:50 push %eax 8048621:68 6e 87 04 08 push $0x804876e 8048626:e8 6d fe ff ff call 8048498 <printf@plt>说明几点:
(1)经过反汇编分析对于常量区中的字符串,a[0]和*b都是一样的,都要经过两次寻址来获得内容,因为此时a和b都是保存的地址;
全局区字符串
代码片段如下
char a[] = "hello";int main(){ char* b = a; printf("%c\n", a[0]); printf("%c\n", *b); return 0;}反汇编部分代码如下
80485ea:83 ec 14 sub $0x14,%esp 80485ed:c7 45 f4 40 99 04 08 movl $0x8049940,-0xc(%ebp) #存入到b 80485f4:0f b6 05 40 99 04 08 movzbl 0x8049940,%eax #直接获得a[0]的内容 80485fb:0f be c0 movsbl %al,%eax 80485fe:83 ec 08 sub $0x8,%esp 8048601:50 push %eax 8048602:68 68 87 04 08 push $0x8048768 8048607:e8 8c fe ff ff call 8048498 <printf@plt> 804860c:83 c4 10 add $0x10,%esp 804860f:8b 45 f4 mov -0xc(%ebp),%eax #首先获得地址b 8048612:0f b6 00 movzbl (%eax),%eax #获得b的内容 8048615:0f be c0 movsbl %al,%eax 8048618:83 ec 08 sub $0x8,%esp 804861b:50 push %eax 804861c:68 68 87 04 08 push $0x8048768 8048621:e8 72 fe ff ff call 8048498 <printf@plt>说明几点
(1)对于全局区字符串,a[0]只需要通过一次寻址就可以获得,而*b需要通过两次寻址获得;
全局区中常量字符串
代码片段如下
char* a = "hello";int main(){ char* b = a; printf("%c\n", a[0]); printf("%c\n", *b); return 0;}反汇编部分代码如下
80485ea:83 ec 14 sub $0x14,%esp 80485ed:a1 48 99 04 08 mov 0x8049948,%eax 80485f2:89 45 f4 mov %eax,-0xc(%ebp) 80485f5:a1 48 99 04 08 mov 0x8049948,%eax #首先获得a 80485fa:0f b6 00 movzbl (%eax),%eax #获得a里的内容 80485fd:0f be c0 movsbl %al,%eax 8048600:83 ec 08 sub $0x8,%esp 8048603:50 push %eax 8048604:68 6e 87 04 08 push $0x804876e 8048609:e8 8a fe ff ff call 8048498 <printf@plt> 804860e:83 c4 10 add $0x10,%esp 8048611:8b 45 f4 mov -0xc(%ebp),%eax #获得b 8048614:0f b6 00 movzbl (%eax),%eax #获得b中的内容 8048617:0f be c0 movsbl %al,%eax 804861a:83 ec 08 sub $0x8,%esp 804861d:50 push %eax 804861e:68 6e 87 04 08 push $0x804876e 8048623:e8 70 fe ff ff call 8048498 <printf@plt>
说明几点
(1)对于全局区的常量字符串,a[0]和*b都是一样的,都要经过两次寻址来获得内容;
(2)对于堆区申请的字符串,使用a来指向该分配的内存,a[0]和*b都是一样的,都要经过两次寻址来获得内容,因为此时a和b都是保存的地址;
循环赋值
下标方式
代码片段如下
int a[5];int b[5];int main(){ for(int i=0; i < 5; ++i) a[i] = b[i]; return 0;}
反汇编部分代码如下
080485ac <main>: 80485ac:55 push %ebp 80485ad:89 e5 mov %esp,%ebp 80485af:83 ec 10 sub $0x10,%esp 80485b2:c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp) #i赋值为0; 80485b9:eb 18 jmp 80485d3 <main+0x27> #先去判断条件 80485bb:8b 45 fc mov -0x4(%ebp),%eax #获得i 80485be:8b 14 85 e8 98 04 08 mov 0x80498e8(,%eax,4),%edx #直接获得b[i],这边是有一个乘法和加法 80485c5:8b 45 fc mov -0x4(%ebp),%eax #获得i 80485c8:89 14 85 d4 98 04 08 mov %edx,0x80498d4(,%eax,4) #将b[i]赋值给a[i],这边是有一个乘法和加法 80485cf:83 45 fc 01 addl $0x1,-0x4(%ebp) #i加1; 80485d3:83 7d fc 04 cmpl $0x4,-0x4(%ebp) 80485d7:7e e2 jle 80485bb <main+0xf> #继续执行 80485d9:b8 00 00 00 00 mov $0x0,%eax 80485de:c9 leave 80485df:c3 ret
指针方式
代码片段如下
int a[5];int b[5];int main(){ int* p1 = a; int* p2 = b; for(int i = 0; i < 5; ++i) *p1++ = *p2++; return 0;}反汇编部分代码如下
80485b2:c7 45 fc e4 98 04 08 movl $0x80498e4,-0x4(%ebp) #数组a的首地址存入到a中 80485b9:c7 45 f8 f8 98 04 08 movl $0x80498f8,-0x8(%ebp) #数组b的首地址存入到b中 80485c0:c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp) #i值为0 80485c7:eb 1a jmp 80485e3 <main+0x37> #先去判断条件,到e3处 80485c9:8b 45 fc mov -0x4(%ebp),%eax #取地址a 80485cc:8d 50 04 lea 0x4(%eax),%edx #取地址a+4放入到edx 80485cf:89 55 fc mov %edx,-0x4(%ebp) #将a+4放入到原来的局部变量a中,而原先的地址由eax保存 80485d2:8b 55 f8 mov -0x8(%ebp),%edx #取地址b 80485d5:8d 4a 04 lea 0x4(%edx),%ecx #取地址b+4放入到ecx 80485d8:89 4d f8 mov %ecx,-0x8(%ebp) #将b+4放入到原来的局部变量b中,而原先的地址由edx保存 80485db:8b 12 mov (%edx),%edx #取b的内容 80485dd:89 10 mov %edx,(%eax) #将b的内容放入到eax对应的地址中 80485df:83 45 f4 01 addl $0x1,-0xc(%ebp) #i加1 80485e3:83 7d f4 04 cmpl $0x4,-0xc(%ebp) 80485e7:7e e0 jle 80485c9 <main+0x1d>说明几点:
(1)下标方式和指针方式寻址的反汇编可以得出,下标方式直接通过mov 0x80498e8(,%eax,4),%edx来获得内容,但这其实是包括一个乘法和加法后的寻址的;而对于指针寻址,需要两次寻址,首先获得本身的地址,然后还要获得地址的内容;
(2)对于栈中的数组,指针寻址和全局区的数组寻址类似;对于下标寻址,有原来的直接地址变为了帧指针,即变为了类似mov -0x2c(%ebp,%eax,4),%edx;
- 【反汇编分析】下标寻址和指针寻址
- C++反汇编学习笔记7——数组和指针以及他们的寻址
- 基于arm的C++反汇编 数组和指针的寻址
- 数组和指针寻址
- 汇编寄存器和寻址方式简介
- 汇编寄存器和寻址方式简介
- 汇编_8086/8088寻址方式和指令系统
- 8086汇编笔记:寄存器和寻址
- 51汇编----寻址方式和伪指令
- 网易云课堂《Linux内和分析》汇编分析和寻址方式小结(一)
- 寻址
- 寻址
- 汇编 寻址方式总结
- [汇编]8086寻址方式
- 汇编寻址方式总结
- 汇编 寻址方式总结
- 汇编寻址方式
- ARM汇编--寻址方式
- 【hiho一下 第四十一周】骨牌覆盖问题·一
- opencv FullScreen curlib
- bat
- 嵌入式Linux-yum
- 聊天内容的动态加载
- 【反汇编分析】下标寻址和指针寻址
- android视频旋转处理方法
- C编译器剖析_5.3.1 中间代码生成及优化_If语句和复合语句的翻译
- 3-7(1)
- SDKD SingleTest B 题解 2015-04-03
- 将安卓驱动编译成moudle开机后自动启动
- 测土配方施肥专家系统
- Base64的java实现
- C++单例模式的创建