指针小记
针对为什么传一级空指针进函数不会改变指针的指向而引发的思考
首先先看下面的错误代码
1 #include <iostream> 2 using namespace std; 3 #include <stdlib.h> 4 #include <string.h> 5 void func(int *p) 6 { 7 p = (int *)malloc(sizeof(int) * 10); 8 memset(p, 0, sizeof(p)); 9 p[0] = 1; 10 } 11 int main() 12 { 13 int *p = NULL; 14 func(p); 15 cout << p[0] << endl; 16 return 0; 17 }
一个很简单的函数,就是给*p在函数中分配空间并将p[0]置成1,最后打印输出p[0]。但是运行的结果却是segmengt fault。
我们通过查看这段程序的汇编代码来分析一下出现段错误的原因。
1 Dump of assembler code for function func(int*): 2 0x00000000004008cd <+0>: push %rbp 3 0x00000000004008ce <+1>: mov %rsp,%rbp 4 0x00000000004008d1 <+4>: sub $0x20,%rsp 5 0x00000000004008d5 <+8>: mov %rdi,-0x18(%rbp) 6 0x00000000004008d9 <+12>: mov $0x28,%eax 7 0x00000000004008de <+17>: mov %rax,%rdi 8 0x00000000004008e1 <+20>: callq 0x400780 <malloc@plt> 9 0x00000000004008e6 <+25>: mov %rax,-0x8(%rbp) 10 0x00000000004008ea <+29>: mov -0x8(%rbp),%rax 11 0x00000000004008ee <+33>: mov $0x8,%edx 12 0x00000000004008f3 <+38>: mov $0x0,%esi 13 0x00000000004008f8 <+43>: mov %rax,%rdi 14 0x00000000004008fb <+46>: callq 0x400750 <memset@plt> 15 0x0000000000400900 <+51>: mov -0x8(%rbp),%rax 16 0x0000000000400904 <+55>: movl $0x1,(%rax) 17 0x000000000040090a <+61>: leaveq 18 0x000000000040090b <+62>: retq 19 End of assembler dump.
重点放在
1 sub $0x20,%rbp 2 mov %rdi,-0x18(%rbp)
sub $0x20,%rbp的意思是给栈分配0x20大小的空间。
而mov %rdi,-0x18(%rbp)的意思是把函数的第一个参数的值压入栈中存储。
这说明了什么?说明了函数中的*p其实是一个临时变量,和主函数并不是同一个*p了。给临时变量申请内存并赋值当前不能反映到主函数的*p上,所以主函数的*p还是个空指针,而打印空指针当然就段错误了。
为什么二级指针就可以了呢?
1 #include<stdio.h> 2 #include<stdlib.h> 3 void getMemery(int **p) 4 { 5 /*申请1024个int大小*/ 6 *p = malloc(sizeof(int)*1024); 7 if(NULL == *p) 8 { 9 printf("malloc failed 10 "); 11 *p = NULL; 12 } 13 } 14 int main(void) 15 { 16 int *p = NULL; 17 getMemery(&p); 18 printf("address of p is %p 19 ",p); 20 free(p); 21 p = NULL; 22 return 0; 23 }
从运行结果可以看到,p的值被改变了,而不再是初始的NULL。