注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。
测试环境:Ubuntu 10.10
GCC版本:4.4.5
一、野指针
1) 指针变量中的值是非法的内存地址,进而形成野指针
2) 野指针不是NULL指针,是指向不可用内存地址的指针(比如函数返回值为数组)
3) NULL指针并无危害,很好判断,也很好调试
4) C语言中无法判断一个指针所保存的地址是否合法
二、野指针的由来
1) 局部指针变量没有被初始化
2) 指针所指向的变量在指针之前被销毁
3) 使用已经释放过的指针
4) 进行了错误的指针运算
5) 进行了错误的强制类型转换
实例分析
野指针初探
41-1.c
#include <stdio.h>
#include <malloc.h>
int main()
{
//40字节,10个int
int* p1 = (int*)malloc(40);
//这个内存地址也不能访问,如果访问这块地址内存会有运行错误
int* p2 = (int*)1234567;
int i = 0;
for(i=0; i<40; i++) //访问40个int,长度错误
{
*(p1 + i) = 40 - i; //由于指针运算产生了野指针,导致指针越界访问
}
free(p1); //释放内存,但是p1中存储内容没变,p1因此变成野指针
for(i=0; i<40; i++)
{
p1[i] = p2[i]; //段错误,程序崩溃,p1没有内存,p2[]不能访问
}
return 0;
}
操作:
1) gcc 41-1.c -o 41-1.out编译正确,打印结果:
Segmentation fault (core dumped)
分析:
p1[i]内存被释放后仍然被赋值,对野指针操作非法;p2[i]指向了操作系统不允许放的内存也会报错。
2) 添加代码:
#include <stdio.h>
#include <malloc.h>
int main()
{
//40字节,10个int
int* p1 = (int*)malloc(40);
//这个内存地址也不能访问,如果访问这块地址内存会有运行错误
int* p2 = (int*)1234567;
int i = 0;
printf("p1 = %p\n",p1);
for(i=0; i<40; i++) //访问40个int,长度错误
{
*(p1 + i) = 40 - i; //由于指针运算产生了野指针,导致指针越界访问
}
free(p1); //释放内存,但是p1中存储内容没变,p1因此变成野指针
printf("p1 = %p\n",p1);
for(i=0; i<40; i++)
{
p1[i] = p2[i]; //段错误,程序崩溃,p1没有内存,p2[]不能访问
}
return 0;
}
gcc 41-1.c -o 41-1.out编译正确,打印结果:
p1 = 0x8ff8008
p1 = 0x8ff8008
Segmentation fault (core dumped)
分析:
p1指向的内存被释放后,仍然保存之前指向的地址,最好用NULL重新赋值。
三、基本原则
1) 绝不返回局部变量和局部数组的地址
2) 任何变量在定义后必须0初始化
3) 字符数组必须确认0结束符后才能成为字符串
4) 任何使用与内存操作相关的函数必须指定长度信息
实例分析
无处不在的野指针
41-2.c
#include <stdio.h>
#include <string.h>
#include <malloc.h>
struct Student
{
char* name;
int number;
};
char* func()
{
char p[] = "D.T.Software";
return p; //返回字符数组名,一定会产生野指针
}
void del(char* p)
{
printf("%s\n", p);
free(p);
}
int main()
{
struct Student s; //结构体没有初始化,name成为野指针
char* p = func(); //函数返回局部变量地址——野指针
strcpy(s.name, p); //拷贝字符串,使用野指针(产生段错误)
s.number = 99;
p = (char*)malloc(5); //申请五个内存
strcpy(p, "D.T.Software"); //产生内存越界,操作了野指针指向的内存空间(这里不会产生段错误,但是操作很危险)
del(p);
return 0;
}
操作:
1) gcc 41-2.c -o 41-2.out编译有警告:
41-2.c:15:2: warning: function returns address of local variable [-Wreturn-local-addr]
return p;
^
警告:函数返回局部变量地址
运行错误:
Segmentation fault (core dumped)
小结:
内存错误是实际产品开发中最常见的问题,然而绝大多数的bug都可以通过遵循基本的编程原则和规范来避免。
因此,在学习的时候要牢记和理解内存操作的基本原则,目的和意义。