深入解析C语言中的内存分配相关问题
C内存分配区域
全局数据区 栈区 堆区 命令行参数区 示例 #include <stdio.h> int a = 0; // 静态存储区(初始化区域) char *p1; // 静态存储区(未初始化区域) void example() { int b; // 栈区 char s[] = "abc"; // 栈区 char *p2; //栈区 static int b = 0; // 静态存储区(初始化区域) // 分配得来的10和20字节的区域在堆上 p1 = (char *)malloc(10); p2 = (char *)malloc(10); } 图示 注意 在嵌入式系统中有ROM和RAM两类内存,程序被固化进ROM,变量和堆栈设在RAM中,用const定义的常量也会被放入ROM中
什么是局部变量、全局变量和静态变量? 顾名思义,局部变量就是在一个有限的范围内的变量,作用域是有限的,对于程序来说,在一个函数体内部声明的普通变量都是局部变量,局部变量会在栈上申请空间,函数结束后,申请的空间会自动释放。而全局变量是在函数体外申请的,会被存放在全局(静态区)上,知道程序结束后才会被结束,这样它的作用域就是整个程序。静态变量和全局变量的存储方式相同,在函数体内声明为static就可以使此变量像全局变量一样使用,不用担心函数结束而被释放。 void *malloc(size_t size); void free(void *p); /*一般这样用 Struct elem *p; p = (struct elem*)malloc(sizeof(struct elem)) void free(p) */ malloc原理 malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要进行返回值的判断。 malloc的使用要点 函数malloc的原型如下: void * malloc(size_t size);
int *p = (int *)malloc(sizeof(int) * length);
malloc返回值的类型是void *,所以在调用malloc时要显式地进行类型转换,将void* 转换成所需要的指针类型 直接搬运的代码,确实很好!!容易理解 //main.cpp int a = 0; //全局初始化区 char *p1; //全局未初始化区 main() { int b; //栈 char s[] = "abc"; //栈 char *p2; //栈 char *p3 = "123456"; //123456 在常量区,p3在栈上。 static int c =0;//全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20);//分配得来得10和20字节的区域就在堆区。 strcpy(p1,"123456"); //123456 放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 } 此外,还有realloc(重新分配内存)、calloc(初始化为0)、alloca(在栈上申请内存,自动释放)等。 内存的规范种类 常规类(Conventional Memory) 高位内存(Upper Memory) 高端内存区(High Memory Area) 扩展内存块(Extened Memory Block) 内存分配方式
常见的内存错误及对策 内存分配未成功,却使用了它 t = (struct btree *)malloc(sizeof(struct btree)); if (t == NULL) { printf("内存分配失败!n"); exit(EXIT_FAILURE); }
忘记释放内存,导致内存泄漏 含有这种错误的函数每被调用一次就丢失一块内存。刚开始的时候,系统内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽 释放了内存却继续使用它 程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面 规则 用malloc或new申请内存之后,应该立即检查指针是否为NULL。防止使用指针为NULL的内存
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命周期内保持不变,只有数组的内容可以改变 指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。 修改内容 #include <stdio.h> int main() { char a[] = "hello"; a[0] = 'x'; printf("%sn",a); char *p = "wrold"; p[0] = 'x'; printf("%sn",p); return 0; }
语句p = a并不能把a的内容复制指针p,而是把a的地址赋给了p。要想复制a的内容,可以先用库函数malloc为p申请一块容量为strlen(a)1个字符的内存,再用strcpy进行字符串复制。同理,语句if(p == a)比较的不是内容而是地址,应该用库函数strcmp来比较 #include <stdio.h> #include <string.h> #include <stdlib.h> int main() { char a[] = "hello"; char b[10]; strcpy(b,a); //不能用b = a int len = strlen(a); char *p = (char *)malloc((len + 1) * sizeof(char)); strcpy(p,a); if (strcmp(p,a) == 0) { printf("p和a是相等的!n"); } free(p); return 0; }
注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。不论数组a的容量是多少,sizeof(a)始终等于sizeof(char *) #include <stdio.h> #include <string.h> #include <stdlib.h> void funC(char *a); int main() { char a[] = "hello"; char *p = a; printf("%dn",sizeof(a)); // 6字节 printf("%dn",sizeof(p)); // 4字节 funC(a); return 0; } void funC(char *a) { printf("%dn",sizeof(a)); // 4字节而不是6字节 }
指针参数是如何传递内存的 代码 #include <stdio.h> #include <string.h> #include <stdlib.h> void GetMemory(char *p,int num) { p = (char*)malloc(sizeof(char) * num); } char* getMemory(char *p,int num) { p = (char *)malloc(sizeof(char) * num); return p; } int main() { char *str = NULL; str = getMemory(str,200); strcpy(str,"hello world!"); //运行错误 printf("%s",str); free(str); return 0; }
原因 改进 #include <stdio.h> #include <string.h> #include <stdlib.h> void GetMemory(char *p,"hello world!"); //运行错误 printf("%sn",str); free(str); return 0; }
示例: #include <stdio.h> #include <string.h> #include <stdlib.h> void GetMemory(char *p,int num) { p = (char *)malloc(sizeof(char) * num); return p; } char* getArray(void) { char p[] = "hello world!"; return p; // 编译器提出警告 } int main() { char *str = NULL; str = getArray(); printf("%sn",str); // str指向的内容是垃圾 free(str); return 0; } 杜绝“野指针” 指针变量没有初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应该被初始化,要么将指针设置为NULL,要么让它指向合法的内存,例如: char *p = NULL; char *str = (char *)malloc(sizeof(char) * 100); 指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针
判断指针是否为NULL,如果是则马上用return语句终止本函数。例如: char* getPoint() { char *p = malloc(sizeof(char) * 100); if (p == NULL) { return null; } } 判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行(我经常用也是推荐做法): char* getPoint() { char *p = malloc(sizeof(char) * 100); if (p == NULL) { exit(1); } } 为new和malloc设置异常处理函数
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |