C语言学习笔记--内存操作常见错误
1. 野指针
(1)指针变量中的值是非法的内存地址,进而形成野指针
(2)野指针不是 NULL 指针,是指向不可用内存地址的指针
(3)NULL 指针并无危害,很好判断,也很好调试
(4)C 语言中无法判断一个指针所保存的地址是否合法
2. 野指针的由来
(1)局部指针变量没有被初始化
(2)指针所指向的变量在指针之前被销毁
(3)使用己经释放过的指针
(4)进行了错误的指针运算
(5)进行了错误的强制类型转换
#include <stdio.h> #include <malloc.h> int main() { int* p1 = (int*)malloc(40); //这里只分配40字节 int* p2 = (int*)1234567; //该地址是非法的,进行了错误的强制转换 int i = 0; for(i = 0;i < 40; i++) { *(p1 + i) = 40 - i; //越界访问,p1所指内存总共才40字节,而这里要写入40*4字节 } free(p1); for(i = 0; i < 40; i++) { p1[i] = p2[i]; //p1越界访问,p2非法内存 } return 0; }
3. 基本原则
(1)绝不返回局部变量和局部数组的地址
(2)任何变量在定义后必须用 0 初始化
(3)字符数组必须确认 0 结束符后才能成为字符串
(4)任何使用与内存操作相关的函数必须指定长度信息
#include <stdio.h> #include <malloc.h> #include <string.h> struct Student { char* name; int number; }; char* func() { char p[] = "Hello World!"; 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结构体的name指针是野指针! s.number = 99;//合法 p = (char*)malloc(5); strcpy(p, "Hello World!");//越界,源字符串比目标内存大! del(p); return 0; }
4.常见内存错误
(1)结构体成员指针未初始化
(2)结构体成员指针未分配足够的内存
(3)内存分配成功,但并未初始化
(4)内存操作越界
#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);//不合符谁申请谁释放原则 //当p指向一个数组时,该操作是非法的,因为 //只能free堆中的内存。 } void func(unsigned int size) { int* p = (int*)malloc(size * sizeof(int)); int i = 0; if(size % 2 != 0) //当size为奇数时会p产生内存泄漏 { 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); //test内部释放了p所指内存,不符合谁申请谁释放原则 free(p); //这里重复释放p,会造成程序崩溃 func(9); //参数为奇数,会造成内存泄漏! func(10);//正常 return 0; }
5.内存操作的交通规则
(1)动态内存申请之后,应该立即检查指针的值是否为 NULL,防止使用 NULL 指针
int* p = (int*)malloc(56); if (p != NULL){ //do something here! } ……
(2)free 指针之后必须立即赋值为 NULL。(如 free(p);p=NULL;)
(3)任何与内存操作相头的函数都必须带长度信息!!!!!!!!!
void printf(int* p, int size) //带长度信息size { int i = 0; snprintf(buf,sizeof(buf), "%s\n", "Hello World!"); for(i = 0;i< size; i++){ //长度信息size printf("%s\n",p[i]) } }
(4)malloc 操作和 free 操作必须匹配,防止内存泄露和多次释放。并且遵循谁申请谁释放的原则。
#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] = i; //d1.p指针未被初始化,内存空间也未分配 } 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; }
内存错误的本质源于指针保存的地址为非法值(如指针未初始化或指针运算导致的越界)。内存泄漏源于malloc和free不匹配。当malloc次数多于free里产生内存泄漏。反之程序可能崩溃。
参考资料:
www.dt4sw.com
http://www.cnblogs.com/5iedu/category/804081.html