标签:
本节研究数组下标寻址和指针寻址的相关问题;
(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