- 内存分配方式
内存分配方式有三种:
- 从静态存储区域分配。存储在程序编辑的时候就已经分配好了,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
- 在栈上创建。在执行函数时,函数内部变量的存储单元都可以在栈上创建,函数执行结束之后这些存储单元会自动被释放。栈内存分配运算内置于处理器指令集中,效率更高,但是栈内存容量有限。
- 在堆上分配内存,亦称动态分配内存。程序在运行是可以使用malloc或new来申请任意多的内存,程序员负责在何时使用free或delete来释放内存。动态内存生存期有我们自由控制,使用更加自由,但是问题也会越多。
- 常见的内存错误及其策略
发生内存错误是一件非常麻烦的事情。编辑器不会自动的发现错误,通常在程序运行时才能捕捉到。
常见的内存错误及其策略如下:
- 内存分配未成功,却使用了它。编程新手常犯这样的错误,因为他们意识不到分配内存会不成功。常用的解决办法是,在使用内存前检查指针是否为null。如果指针p是作为函数的参数,那要在函数的入口使用assert(p != NULL)检查。如果使用malloc或new还申请的内存,应该使用if(p == UNLL)或者if(P != UNLL)来进行防范。
- 内存虽然申请成功,但尚未初始化就引用它。犯这种错误主要有两个起因:一是没有初始化的观念,二是误以为内存缺省初值全为零,导致引用初值错误(例如数组)。内存的缺省初值没有一个统一的标准,尽管有的时候是零,但是宁可信其无不可信其有。所以,无论我们是以何种方式创建的数组,我都要对其进行赋初值,就算是零赋值也不可以省略。
- 内存申请成功也初始化了,但是操作越过了内存的边界。
- 忘记释放内存,导致内存泄漏。含这种错误的函数每一次被调用都会丢失一块内存,刚开始你的内存充足,看不到错误。但是终有一次程序会崩溃,提示你内存耗尽。动态内存的申请和释放一定要成对,程序中malloc和free的次数一定是相同的。(new和delete也是一样)
- 释放内存之后,还在使用它。这种情况有三种:
1.程序中的对象调用关系太复杂,很难搞清楚某个对象是否已经释放了内存,因此要重新设计数据结构,从根本上解决对象管理的混乱局面。
2.函数的return语句写错了,注意不要返回栈内存的指针或者引用,因为该内存在函数体结束的时候就被自动销毁了
- 使用free或delete释放内存之后,没有将指针设置为UNLL。导致产生野指针。
- 指针与数组的对比
C++/C程序中,指针和数组在不少地方可以互相替换使用,会让人产生错觉,以为两者是等价的。
数组要么在静态存储区被创建(静态数组),要么在栈上被创建。数组名对应着(不是指向)一块内存,内存的地址和容量在生命周期保持不变,但是其内容是可以改变的。
指针可以指向任意类型的内存块,他的特征就是”可变“,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。
char a[] = "hello";
a[0] = 'X';
cout << a << endl;
char *p = "world"//注意p指向的是常量字符串
p[0] = 'X';//编辑器不能发现该错误
cout << p << endl;
a的内容可以改变。指针p指向常量字符串”world”,常量字符串的内容是不可以被改变的。从语法上看,编辑器并不会觉得有问题,但是该语句企图修改常量字符串的内容而导致运行错误。
//数组
char a[] = "hello";
char b[10];
strcpy(b,a);//在这里不能用b = a;
//指针
int len = strlen(a);
char * p = (char *)malloc(sizeof(char)* (len + 1));
strcpy(p,a);//不要用p = a;