那么野指针到底是怎么来的呢?来源有这么几方面:1、局部指针变量没有被初始化;2、指针所指向的变量在指针之前被销毁;3、使用已经释放过的指针;4、进行了错误的指针运算;5、进行了错误的强制类型转换。
下来我们以代码为例进行分析,代码如下
#include <stdio.h>
#include <malloc.h>
int main()
{
int* p1 = (int*)malloc(40);
int* p2 = (int*)1234567;
int i = 0;
for(i=0; i<40; i++)
{
*(p1 + i) = 40 - i;
}
free(p1);
for(i=0; i<40; i++)
{
p1[i] = p2[i];
}
return 0;
}我们看到第 7 行进行了错误的强制类型转换,在第 12 行进行指针的赋值,但是由于申请的只有40字字节的大小,但是我们进行40个 int 类型的数赋值,这个进越界啦。我们在第 15 行释放了指针 p1,但是在第 19 行又使用了指针 p1,这也会出错。我们看看编译后的结果
我们看到直接报段错误了,这便是操作了野指针带来的影响。我们在 C 语言中有这么几个基本原则:a> 绝不返回局部变量和局部数组的地址;b> 任何变量在定义后必须 0 初始化;c> 字符数组必须确认 0 结束符后才能成为字符串;d> 任何使用与内存相关的函数必须指定长度信息。
我们再看一份代码,在这份代码里野指针更是无处不在
#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;
char* p = func();
strcpy(s.name, p);
s.number = 99;
p = (char*)malloc(5);
strcpy(p, "D.T.Software");
del(p);
return 0;
}我们首先在第27行定义了一个结构体变量 s,但是结构体里的指针类型的成员变量没进行初始化。接着定义指针 p 并调用 func 函数进行初始化,但是在 func 函数中返回了局部数组的地址。在第34行申请了5个char*类型大小的空间,在第36行进行字符串的复制,但是越界了。我们看到在这份代码中,野指针是无处不在的。
内存错误是实际产品开发中最常见的问题,然而绝大多数 bug 都可以通过遵循基本的编程原则和规范来避免。因此,我们在学习的时候要牢记和理解内存操作的基本原则,目的和意义,这样才能达到最小程度的减少 bug。
常见的内存错误有:a> 结构体成员指针未初始化;b> 结构体成员指针未分配足够的内存;c> 内存分配成功,但并未初始化;d> 内存操作越界。
我们再以代码为例进行分析说明
#include <stdio.h>
#include <malloc.h>
void test(int* p, int size)
{
int i = 0;
for(i=0; i<size; i++)
{
printf("%d\n", p[i]);
}
free(p);
}
void func(unsigned int size)
{
int* p = (int*)malloc(size * sizeof(int));
int i = 0;
if( size % 2 != 0 )
{
return;
}
for(i=0; i<size; i++)
{
p[i] = i;
printf("%d\n", p[i]);
}
free(p);
}
int main()
{
int* p = (int*)malloc(5 * sizeof(int));
test(p, 5);
free(p);
func(9);
func(10);
return 0;
}我们看到在程序的第37行申请了堆空间 ,用指针 p 指向这片堆空间。调用了 test 函数,但是在 test 函数内部进行了指针的释放。这是错误的 ,free 是用来释放堆上申请的空间的,但在这块我们释放的是栈上的内存,会造成段错误。我们在第41行继续释放 p,造成了指针的重复释放。我们在 func 函数中如果传入的是奇数便直接返回,申请的内存空间也没有释放,这样会造成内存泄漏。
编译结果如下

再来看一个程序
#include <stdio.h>
#include <malloc.h>
struct Demo
{
char* p;
};
int main()
{
struct Demo d1;
struct Demo d2;
char i = 0;
for(i='a'; i<'z'; i++)
{
d1.p[i] = 0;
}
d2.p = (char*)calloc(5, sizeof(char));
printf("%s\n", d2.p);
for(i='a'; i<'z'; i++)
{
d2.p[i] = i;
}
free(d2.p);
return 0;
} 我们看到定义的两个结构体,他们的指针成员变量没有进行初始化,在第21行申请堆空间,在第27行进行赋值,但是内存越界了。我们看看编译结果
同样直接报段错误。那么我们在动态内存申请之后,应立即检查指针值是否为 NULL,防止使用 NULL 指针。free 指针之后必须立即赋值为 NULL!任何与内存操作相关的函数都必须带长度信息。malloc 操作和 free 操作必须匹配,防止内存泄漏和多次释放。
内存错误的本质来源于指针保存的地址为非法值,指针变量为初始化,保存随机值。指针运算导致内存越界。内存泄漏源于 malloc 和 free 不匹配,当 malloc 次数多于 free时,产生内存泄漏;当 malloc 次数少于 free 时,程序可能崩溃!
欢迎大家一起来学习 C 语言,可以加我QQ:243343083。
原文:http://blog.51cto.com/12810168/2110685