c语言野指针导致问题,C语言野指针和非法内存操作分析

本文详细解析了C语言中常见的内存管理问题,包括野指针、内存泄漏、非法内存操作等,并提供了相应的错误示例。强调了初始化指针、避免数组越界、正确释放内存的重要性。同时,提出了如检查NULL指针、限制函数出口、释放后设指针为NULL等最佳实践,以减少程序运行时的隐患。
摘要由CSDN通过智能技术生成

初识野指针

l 野指针通常是因为指针变量中保存的值不是一个合法的内存地址而造成的。

l 野指针不是NULL指针,是指向不可用内存的指针。

l NULL指针不容易用错,因为if语句很好判断一个指针是不是NULL。

l C语言中没有任何手段可以判断一个指针是否为野指针。

野指针的由来

(1)局部指针变量没有被初始化。

例:

include

include

struct Student

{

char* name;

int number;

};

int main()

{

struct Student s;

strcpy(s.name, "Delphi Tang"); // OOPS!

s.number = 99;

return 0;

}

(2)使用已经释放过的指针。

例:

include

include

include

void func(char* p)

{

printf("%s\n", p);

free(p);

}

int main()

{

char* s = (char*)malloc(5); //内存越界

strcpy(s, "Delphi Tang");

func(s);

printf("%s\n", s); // OOPS!

return 0;

}

使用释放过的指针,很有可能使一些其他程序被莫名的操作。

(3)指针所指向的变量在指针之前被销毁。

例:

include

char* func()

{

char p[] = "Delphi Tang"; //保存在栈区,使用之后就会被释放

return p;

}

int main()

{

char* s = func(); //s指向一段被释放了的栈空间。这段空间若是没有被占用,还是会打印Delphi Tang,若是被占用,打印什么,就变得未知了。这时候的s就变成了野指针。

printf("%s\n", s); // OOPS!

return 0;

}

二.经典错误

非法内存操作分析

l 结构体成员指针未初始化

l 没有为结构体指针分配足够的内存(不能越界)

include

include

struct Demo

{

int* p;

};

int main()

{

struct Demo d1;

struct Demo d2;

int i = 0;

for(i=0; i<10; i++)

{

d1.p[i] = 0; // OOPS!

}

//p是未初始化,没有分配地址的指针,也就是野指针,直接给它赋值,是错误的。

d2.p = (int*)calloc(5, sizeof(int));

for(i=0; i<10; i++)

{

d2.p[i] = i; // OOPS!

}

//分配了5个空间,赋值10次。若是这段内存后面的内容没人用,那我们用了也没事。但是,我们若使用了,就会改写了其他的变量。

free(d2.p);

return 0;

}

内存初始化分析

l 内存分配成功,但是并没有初始化

include

include

int main()

{

char* s = (char*)malloc(10);

printf(s); // OOPS!

free(s);

return 0;

}

字符串的特点是,以/0结尾,但是我们申请下来的这个字符串是不是以/0结尾确实不一定的。

内存越界分析--数组越界

include

void f(int a[10])

{

int i = 0;

for(i=0; i<10; i++)

{

a[i] = i; // OOPS!

printf("%d\n", a[i]);

}

}

//首先这种方式合理,因为数组在传递而定过程中,会变为指针。但在赋值的时候就出现了错误。

int main()

{

int a[5];

f(a);

return 0;

}

内存泄漏分析

include

include

void f(unsigned int size)

{

int* p = (int*)malloc(size*sizeof(int));

int i = 0;

if( size % 2 != 0 )

{

return; // OOPS!这里面的空间没有释放,造成了内存的损失。

}

for(i=0; i

{

p[i] = i;

printf("%d\n", p[i]);

}

free(p);

}

int main()

{

f(9);

f(10);

return 0;

}

设计函数的时候,最好是单入口,单出口。内存泄漏时工程里很容易忽视的。

为了解决这个问题,我们将上面的函数改成单入口,单出口:

include

include

void f(unsigned int size)

{

int* p = (int*)malloc(size*sizeof(int));

int i = 0;

if( size % 2 == 0 )

{

for(i=0; i

{

p[i] = i;

printf("%d\n", p[i]);

}

}

free(p);

}

int main()

{

f(9);

f(10);

return 0;

}

多次释放指针

include

include

void f(int* p, int size)

{

int i = 0;

for(i=0; i

{

p[i] = i;

printf("%d\n", p[i]);

}

free(p);

}

int main()

{

int* p = (int*)malloc(5 * sizeof(int));

f(p, 5);

free(p); // OOPS!

return 0;

}

我们这里做一个习惯的要求---谁申请谁释放,main函数中申请的动态空间,就在main函数中释放。多次释放指针会造成异常退出。

使用已经释放的指针

include

include

void f(int* p, int size)

{

int i = 0;

for(i=0; i

{

printf("%d\n", p[i]);

}

free(p);

}

int main()

{

int* p = (int*)malloc(5 * sizeof(int));

int i = 0;

f(p, 5);

for(i=0; i<5; i++)

{

p[i] = i; // OOPS!

}

return 0;

}

释放空间后的指针,就变成了野指针。

三. C语言中的交通规则

用malloc申请了内存之后,应该立即检查指针是否我NULl,防止使用值为NULL的指针

int* p = (int*)malloc(5 * sizeof(int));

if( p != NULL)

{

//do something here

}

free(p);

牢记数组的长度,防止数组越界,考虑使用柔性数组

typedef struct _soft_array

{

int len;

int array[];

}SoftArray;

int i = 0;

SoftArray* sa = (SoftArray)malloc(sizeof(SoftArray)+sizeof(int)10);

sa->len = 10;

for(i=0;ilen;i++)

{

sa->array[i] = i + 1;

}

动态申请的操作必须和释放的操作相匹配,方式内存泄漏和多次释放

void f()

{

int* p = (int*)malloc(10);

free(p);

}

int main()

{

int* p = (int*)malloc(10);

f();

free(p);

return 0;

}

为了解决,我们究竟是在主函数还是子函数中释放,我们可以在子函数中加第三个参数,来判断是否需要释放。

free指针之后必须立即赋值为NULL

int* p = (int*)malloc(10);

free(p);

p = NULL;

小结:指针的问题,一般在编译的过程中是找不出来问题的,只有在运行的过程中才会把问题显现出来,所以这里面的一些规则我们是要牢记的,这样可以使我们在工程中,少走很多的弯路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值