上次我们学习了动态内存函数,今天我们要更深一层次的研究学习它。
一。常见的动态内存错误。
(1)对NULL指针的解引用操作。
void test() { int *p=(int*)malloc(INT_MAX/4); *p=20;//如果p的值是NULL,就会有问题 free(p);
解析:没有判断p是否为空,因为只要用malloc开辟空间就需要判空,要不然会出错。
(2)对动态开辟空间的越界访问。
void test() { int i=0; int *p=(int *)malloc(10*sizeof(int)); if(NULL==p) { exit(EXIT_FAILURE); } for(I=0;i<=10;i++) { *(p+i)=I;//当I是10的时候越界访问。 } free(p); }
解析:只开辟了10个空间,但是却用了11个,所以越界访问了。
(3)对非动态开辟内存使用free释放。
void test() { int a=10; int *p=&a; free(p); }
解析:因为p实在栈上开辟的,但是free只能释放堆上的。
(4)使用free释放一块动态开辟内存的一部分。
void test() { int *p=(int *)malloc(100); p++; free(p);//p不在指向动态内存的初始位置。 }
解析:p必须要整体释放,不能局部释放,而且也没有进行判空。
(5)对同一块动态内存多次释放。
void test() { int *p=(int *)malloc(100); free(p); free(p); }解析:重复释放。
(6)动态开辟内存忘记释放(内存泄漏)
void test() { int *p=(int *)malloc(100); if(NULL!=p) { *p=20; } } int main() { test(); while(1); }
解析:没有用free释放,会造成内存泄漏。
二。接下来我们再来看一些经典的笔试题。
(1)
void GetMemory(char *p) { p=(char *)malloc(100); } void Test(void) { char *str=NULL; GetMetmory(str); strcpy(str,"hello world"); printf(str); }解析:p是在调用函数时产生的临时变量,这时str传给p的是它的值,是给p在堆上用malloc,与str没关系,所以函数退出之后p就会被释放,所以发生了内存泄漏,这时str还是空,所以本题打印不出来字符串,会崩溃。如图:
(2)
char *GetMemory(void) { char p[]="hello world"; return p; } void Test(void) { char *str=NULL; str=GetMemory(); printf(str); }
解析:p是一个临时变量,调用完就会被释放,虽然被释放但是它并没有被清空,可是却被新调用的printf函数的栈帧覆盖了,所以最后还是打印不出来。
(3)
void GetMemory(char **p,int num) { *p=(char *)malloc(num); } void Test(void); { char *str=NULL; GetMemory(&str,100); strcpy(str,"hello"); printf(str); }
解析:当运用malloc在堆上开辟空间时,必须要判空,而且本题没有释放p,所以会产生内存泄漏。
(4)
void Test(void); { char *str=(char *)malloc(num); strcpy(str,"hello"); free(str); if(str!=NULL) { strcpy(str,"world"); printf(str); } }
解析:释放str后,str肯定不为空,所以条件肯定成立,而且没对malloc判空。
三。柔性数组:结构中的最后一个元素允许是未知大小的数组。
1柔性数组的特点
(1)结构中的柔性数组成员面前必须至少有一个其它成员。
(2)sizeof返回的这种结构大小不包括柔性数组的内存。
(3)包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的与其大小。
typedef struct st_type { int i; int a[]; }type_a; printf("%d\n",sizeof(type_a));
此时结构体的大小为4,柔性数组成员不占空间大小。
2。柔性数组的优点
(1)方便内存释放。
(2)这样有利于访问速度。