码迷,mamicode.com
首页 > 其他好文 > 详细

【反汇编分析】下标寻址和指针寻址

时间:2015-04-21 18:05:14      阅读:225      评论:0      收藏:0      [点我收藏+]

标签:

      本节研究数组下标寻址和指针寻址的相关问题;


基本知识

数组

(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;
输出如下:

0xbf9449f8
0xbf9449f8
0xbf9449f4

示意图如下:

技术分享


数组寻址和指针寻址

栈中字符串

代码片段如下

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;


【反汇编分析】下标寻址和指针寻址

标签:

原文地址:http://blog.csdn.net/skyuppour/article/details/45151187

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!