1、野指针举例
例1:明显的野指针,直接赋值
#include <stdio.h>
int main()
{
int a;
int *p;
*p=10;//错误来源与此处
printf("%d %d\n",a,*p);
return 0;
}
*p没有被初始化,而直接赋值为10,相当于把某一内存中直接存入“10”;
未初始化的*p指向一个随机的内存单元0x2,该内存单元(0x2)中的值(未知)由计算机内存管理的规则决定,不能随意被程序更改。
指针变量*p初始化的意义:定义*p的指向!而不是初始化前的随机指向!!!
例1修改后如下,可以正常运行:
#include <stdio.h>
int main()
{
int a=2;
int *p=&a;//必须初始化
*p=10;
printf("%d %d\n",a,*p);
return 0;
}
例2:未分配内存的野指针
#include <stdio.h>
#include<string.h>
struct test
{
int x;
char *str;
};
int main()
{
struct test a;
a.x=100;
char s[]="hello";
strcpy(a.str,s);//复制字符串s到结构体变量a的数据项str
printf("%d %s\n",a.x,a.str);
return 0;
}
其中,strcpy(a.str,s)的作用是把数组s[ ]的首地址赋值给a.str
但是a.str在未初始化前,指向的是一个内存中的随机地址,不能随便被更改;
未分配a.str的具体指向时(未初始化),a.str就是一种野指针!
#include <stdio.h>
#include<string.h>
#include<malloc.h>
struct test
{
int x;
char *str;
};
int main()
{
struct test a;
a.x=100;
char s[]="hello";
a.str=(char*)malloc(strlen(s)+1);//!!!通过malloc给a.str分配了一端长度为(s+1)的内存空间
strcpy(a.str,s);//复制字符串s到结构体变量a的数据项str
printf("%d %s\n",a.x,a.str);
return 0;
}
malloc 函数其实就是在内存中:找一片指定大小的空间,然后将这个空间的首地址给一个指针变量,这里的指针变量可以是一个单独的指针,也可以是一个数组的首地址, 这要看malloc函数中参数size的具体内容。我们这里malloc分配的内存空间在逻辑上是连续的,而在物理上可以不连续。我们作为程序员,关注的 是逻辑上的连续,其它的,操作系统会帮着我们处理的。
分配空间口的a.str就不再是随意指向的野指针了,就可以用strcpy进行赋值了,程序就不会报错。
例3:不易被发现的野指针
struct test
{
int x;
char *str;
};
int main()
{
struct test a;
a.x=100;
char s[]="hello";
a.str=s;//指针的赋值,数组首元素的地址赋值给a.str
printf("%d %s\n",a.x,a.str);
return 0;
}
该程序编译完成没有bug,并且正常运行!
但是,详细分析:当s[ ]不是一个固定的数组,而是由其它机制带来的数组,例如该数组在被a.str使用时已经被释放掉了;
则a.str指向的是一个程序不具备访问权限的内存单元,a.str就会变成“野指针”!!!如下程序所示:
#include <stdio.h>
#include<string.h>
#include<malloc.h>
struct test
{
int x;
char *str;
};
int main()
{
struct test a;
a.x=100;
char *s=(char*)malloc(10);//第一步,给s分配长度为10的内存地址
strcpy(s,"hello");//第二步,在该地址中存放hell\0
a.str=s;//第三步,将s的地址赋值给a.str
free(s);//第四步,释放掉s的内存空间
printf("%d %s\n",a.x,a.str);//第五步,a.str输出的值是已经被释放掉的内存中的随机值
return 0;
}
程序运行结果:
a.str的输出结果是:?n
实际上对该程序而言,每一次运行输出的a.str的值都不同,因为a.str通过s赋值,但是s的内存又被释放掉了,因此a.str指向的值为内存的随机内容。
如果注释掉free(s),可以尝试一下。
备注:一定要动态分配存储空间!否则一定会有机会错!