下面以一个例子来引出这种错误:
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <string.h>
void func(int *p)
{
p = (int *)malloc(sizeof(int) * 10);
memset(p, 0, sizeof(p));
p[0] = 1;
}
int main()
{
int *p = NULL;
func(p);
cout << p[0] << endl;
return 0;
}
一个很简单的函数,就是给*p
在函数中分配空间并将p[0]
置成1,最后打印输出p[0]
。但是运行的结果却是segmengt fault
。
我们通过查看这段程序的汇编代码来分析一下出现段错误的原因。
Dump of assembler code for function func(int*):
0x00000000004008cd <+0>: push %rbp
0x00000000004008ce <+1>: mov %rsp,%rbp
0x00000000004008d1 <+4>: sub $0x20,%rsp
0x00000000004008d5 <+8>: mov %rdi,-0x18(%rbp)
0x00000000004008d9 <+12>: mov $0x28,%eax
0x00000000004008de <+17>: mov %rax,%rdi
0x00000000004008e1 <+20>: callq 0x400780 <malloc@plt>
0x00000000004008e6 <+25>: mov %rax,-0x8(%rbp)
0x00000000004008ea <+29>: mov -0x8(%rbp),%rax
0x00000000004008ee <+33>: mov $0x8,%edx
0x00000000004008f3 <+38>: mov $0x0,%esi
0x00000000004008f8 <+43>: mov %rax,%rdi
0x00000000004008fb <+46>: callq 0x400750 <memset@plt>
0x0000000000400900 <+51>: mov -0x8(%rbp),%rax
0x0000000000400904 <+55>: movl $0x1,(%rax)
0x000000000040090a <+61>: leaveq
0x000000000040090b <+62>: retq
End of assembler dump.
重点放在
sub $0x20,%rbp
mov %rdi,-0x18(%rbp)
这两句汇编代码上。
sub $0x20,%rbp
的意思是给栈分配0x20
大小的空间。
而mov %rdi,-0x18(%rbp)
的意思是把函数的第一个参数的值压入栈中存储。
这说明了什么?说明了函数中的*p
其实是一个临时变量,和主函数并不是同一个*p
了。给临时变量申请内存并赋值当前不能反映到主函数的*p
上,所以主函数的*p
还是个空指针,而打印空指针当然就段错误了。
下面介绍两种解决方法:
1.函数返回临时指针的地址:
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <string.h>
int* func(int *p)
{
//此时的p是个临时指针
p = (int *)malloc(sizeof(int) * 10);
memset(p, 0, sizeof(p));
p[0] = 1;
return p; //返回地址
}
int main()
{
int *p = NULL;
p = func(p);
cout << p[0] << endl;
return 0;
}
2.传入指向指针的指针:
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <string.h>
//*p存储的是main函数*ptr的地址
void func(int **p)
{
*p = (int *)malloc(sizeof(int) * 10);
memset(*p, 0, sizeof(*p));
*p[0] = 1;
}
int main()
{
int *ptr = NULL;
func(&ptr);
cout << ptr[0] << endl;
return 0;
}