【规则1】用malloc或new申请内存之后,应该立即检查指针值是否为NULL,防止使用指针值为NULL的内存,可以在函数入口处断言检测。
【规则2】不要忘记为数组或动态内存赋初值(比如calloc比malloc就要好),指针初始化为NULL(c++中为0)。
【规则3】避免数组或指针下标越界,特别
当心发生“多1”或者"少1"的 *** 作。
【规则4】动态内存的申请和释放必须配对,防止内存泄露,具体为malloc/calloc/realloc和free配对,new和delete以及delete[]配对。
【规则5】用free或者delete释放内存后,应立即将指针设置为NULL(C++中为0),防止产生“野指针”、"悬垂指针"。
【规则6】遇到不懂得问题及时debug,一般的虫子
debug一下就灰飞烟灭了,一切bug都是浮云而已
内存管理分为 一.内存管理机制 二.内存泄漏机制 三.内存回收机制 内存溢出:程序在申请内存时,没有足够的内存空间供其使用 内存泄漏:程序在申请内存后,无法释放已申请的内存空间,占用有用内存,最终导致内存溢出 内/******************************************************
内存空间的布局◼ 每个应用都有自己独立的内存空间,其内存空间一般都有以下几大区域
- 代码段(代码区)
✓ 用于存放代码
- 数据段(全局区)
✓ 用于存放全局变量等
- 栈空间
✓ 每调用一个函数就会给它分配一段连续的栈空间,等函数调用完毕后会自动回收这段栈空间
✓ 自动分配和回收
- 堆空间
✓ 需要主动去申请和释放
堆空间◼ 在程序运行过程,为了能够自由控制内存的生命周期、大小,会经常使用堆空间的内存
◼ 堆空间的申请\释放
- malloc \ free
- new \ delete
- new [] \ delete []
char *p = (char *) malloc(4);
p[0] = 10;
p[1] = 11;
p[2] = 12;
p[3] = 13;
free(p);
int *p = (int *) malloc(4);
*p = 10;
free(p);
X86环境(32bit)
int *p = new int;
*p = 10
delete p;
char *p = new char;
*p = 10;
delete p;
char *p = new char[4];
delete[] p;
◼ 注意
- 申请堆空间成功后,会返回那一段内存空间的地址
- 申请和释放必须是1对1的关系,不然可能会存在内存泄露
◼ 现在的很多高级编程语言不需要开发人员去管理内存(比如Java),屏蔽了很多内存细节,利弊同时存在
- 利:提高开发效率,避免内存使用不当或泄露
- 弊:不利于开发人员了解本质,永远停留在API调用和表层语法糖,对性能优化无从下手
堆空间的初始化
memset
◼ memset函数是将较大的数据结构(比如对象、数组等)内存清零的比较快的方法
int size = sizeof(int) * 10;
int *p = (int *) malloc(size);
// memory set
memset(p, 0, size);
int *p0 = new int;
int *p1 = new int();
int *p2 = new int(5);
cout << *p0 << endl;
cout << *p1 << endl;
cout << *p2 << endl;
对象的内存0 (mac)下为0
0
5
◼ 对象的内存可以存在于3种地方
-
全局区(数据段):全局变量
-
栈空间:函数里面的局部变量
-
堆空间:动态申请内存(malloc、new等)
◼ 构造函数(也叫构造器),在对象创建的时候自动调用,一般用于完成对象的初始化工作
struct Person {
int m_age;
Person() {
m_age = 0;
cout << "Person()" << endl;
}
Person(int age) {
m_age = age;
cout << "Person(int age)" << endl;
}
};
◼ 特点
-
函数名与类同名,无返回值(void都不能写),可以有参数,可以重载,可以有多个构造函数
-
一旦自定义了构造函数,必须用其中一个自定义的构造函数来初始化对象
◼ 注意
- 通过==malloc分配的对象不会调用构造函数==
struct Person {
int m_age;
Person() {
m_age = 0;
cout << "Person()" << endl;
}
};
int main() {c
Person person0; // Person()
}
//输出 : Person()
看汇编码的话 LLDB 里用 disas
命令
disas
struct Person {
int m_age;
};
int main() {
Person person0; // Person()
person0.m_age = 10;
}
当没有默认的构造函数的时候,可以供汇编代码可以看到,整个汇编代码执行,只有一句 *** 作movl
,没有任何call函数调用的初始化 *** 作。struct Person {
int m_age=8;
};
xa, -0x8(%rbp)
==在某些特定的情况下,编译器才会为类生成空的无参的构造函数==
成员变量的初始化struct Person {
int m_age;
};
// 全局区:成员变量初始化为0
Person g_person;
void test() {
// 栈空间:没有初始化成员变量
// Person person;
// 堆空间:没有初始化成员变量
Person *p0 = new Person;
// 堆空间:成员变量初始化为0
Person *p1 = new Person();
cout << g_person.m_age << endl;
// cout << person.m_age << endl;
cout << p0->m_age << endl;
cout << p1->m_age << endl;
}
析构函数(Destructor)
◼ 析构函数(也叫析构器),在对象销毁的时候自动调用,一般用于完成对象的清理工作
◼ 特点
- 通过malloc分配的对象free的时候不会调用析构函数
◼ 注意
class Person {
int m_age;
public:
// 新的Person对象诞生的象征
Person() {
cout << "Person::Person()" << endl;
}
// 一个Person对象销毁的象征
~Person() {
cout << "~Person()" << endl;
}
};
◼ 构造函数、析构函数要声明为public,才能被外界正常使用
using namespace std;
struct Car {
int m_price;
Car() {
m_price = 0;
cout << "Car::Car()" << endl;
}
~Car() {
cout << "Car::~Car()" << endl;
}
};
struct Person {
private:
int m_age;
Car *m_car;
public:
// 用来做初始化的工作
Person() {
m_age = 0;
m_car = new Car();
cout << "Person::Person()" << endl;
}
// 用来做内存清理的工作
~Person() {
delete m_car;
cout << "Person::~Person()" << endl;
}
};
int main() {
{
Person person;
}
int age = 20;
int *p = &age;
return 0;
}
Car::Car()
Person::Person()
Car::~Car()
Person::~Person()
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)