C/C++内存分布
在程序运行时的五大内存区域
1,栈区
内容:1,函数的形参;2,函数内的局部变量;3,函数调用的返回地址
特点:1,自动分配、自动释放(无需手动管理);2,空间小(默认几 MB),速度极快;3, 遵循后进先出(LIFO);4,函数执行完,栈内存自动回收
2,堆区
内容:手动申请的内存
申请方式:
C:malloc / calloc / realloc
C++:new
释放方式:
C:free()
C++:delete
特点:1,空间大;2,手动管理,不释放会造成内存泄漏;3,分配速度慢
生命周期:手动申请 → 手动释放(或程序结束)
3,数据段(全局,静态)
内容:1,全局变量;2, static 修饰的静态变量(全局 / 局部静态)
特点:1,自动初始化为 0 / 空值;2,程序结束由系统自动释放;3,全局共享,整个程序都能访问
例如:
java
int g_a = 10; // 全局变量 → 全局区(已初始化)
static int s_b = 20; // 静态变量 → 全局区
void func() {
static int s_c = 30; // 局部静态 → 仍在全局区
}
4,常量区
存放内容:字符串常量、const 修饰的全局常量
特点:1,只读,修改会直接程序崩溃;2,相同常量只存一份(节约内存)
生命周期:整个程序运行期间
例如:
java
const char* str = "Hello World";
// "Hello World" 存放在常量区
// str 是指针变量,存在栈区
5,代码区
内容:编译后的二进制机器指令(函数体代码)
特点:1,只读(防止程序被意外修改);2, 共享(多次运行同一个程序,共用一份代码)
生命周期:整个程序运行期间都存在
指针的大小如何计算?
C/C++ 中,指针变量的大小,只和操作系统位数有关,和它指向什么类型无关。原因是指针存放的是内存地址,不管指向声明类型,地址本身的长度是固定的。
| 系统 | 所有指针(int*,char*,void*...)大小 |
|---|---|
| 32位系统 | 4字节 |
| 64位系统 | 8字节 |
注意:
1,数组名≠指针:只有当数组名作为函数参数时,才会退化为指针。
2,strlen 必须保证指针指向的字符串有 \0 结束,否则会越界访问,结果不可控
相关小知识:
java
int** heap_ptr = (int**)malloc(sizeof(int*));
分析:
·int *:整形指针,int**:二级指针
·sizeof(int*):表示一个一级指针的大小,32位系统是4,64位系统为8
·(int**) :强制类型转换,malloc 返回 void*强转为 int**,
意思是:把这个堆地址,当成存放一级指针 (int*)的地址来使用
·heap_ptr 是二级指针变量
全局普通变量【全局:定义在所有函数外面】与全局静态变量有什么区别?
全局普通变量:在所有文件中可见,其他文件可以用extern+变量类型+变量名;就能直接引用
全局静态变量:只在当前源文件可见
注意:在同一个工程项目中,如果两个源文件都定义全局普通变量:int a;会直接报错,如下图所示,在两个完全不同的工程项目中就不会影响;全局静态变量就没问题

变量的创建与析构顺序?
java
using namespace std;
//创建四个类,用于查看变量创建顺序与析构顺序
class A {
public:
A() {
cout << "A" << endl;
}
~A() {
cout << "~A" << endl;
}
private:
int _a;
};
class B {
public:
B() {
cout << "B" << endl;
}
~B() {
cout << "~B" << endl;
}
private:
int _b;
};
class C {
public:
C() {
cout << "C" << endl;
}
~C() {
cout << "~C" << endl;
}
private:
int _c;
};
class D {
public:
D() {
cout << "D" << endl;
}
~D() {
cout << "~D" << endl;
}
private:
int _d;
};
//全局变量
C c;
int main() {
A a;
B b;
static D d;
return 0;
}
//查看变量创建顺序与析构顺序。
注意:局部静态变量在函数结束时最后析构。

计算拷贝构造的次数?
java
class Widget { /* ... */ }; // 假设已实现拷贝构造函数
Widget f(Widget u)//拷贝次数为1 //拷贝次数为5
{
Widget v(u); //拷贝次数为2 //拷贝次数为6 // 拷贝构造 v
Widget w = v; //拷贝次数为3 //拷贝次数为7 // 拷贝构造 w
return w; //拷贝次数为4 //拷贝次数为8 // 返回值优化相关
}
int main() {
Widget x; // 默认构造
Widget y = f(f(x)); //拷贝次数为9 // 嵌套调用 + 拷贝构造
}
解析:考虑系统优化时,
优化时机 :仅发生在函数传参 和函数返回这两个环节。
拷贝次数为4和拷贝次数为5合并,拷贝次数为8和拷贝次数为9合并,所有共7次。
c和c++中内存管理方式与区别
malloc与new的区别:malloc只申请空间,new包含申请空间+构造函数初始化
释放空间时,free只释放空间,delete包含释放空间还调用了析构函数
java
#include<iostream>
using namespace std;
//int main() {
// int* p = new int[3](1);//不可以写成这样,编译器直接报错了
// return 0;
//}
//C++和c中内存管理方式
//int main() {
// //C函数
// int* p1 = (int*)malloc (sizeof(int));
// int* p2 = (int*)malloc(sizeof(int) * 10);
// //释放
// free(p1);
// free(p2);
// //c++操作符
//// int* p3 = new int;//开辟一个空间
// int* p5 = new int(10);//开辟一个空间,初始化为10
// int* p4 = new int[10];//开辟10个空间
//int* p = new int[3](1);//不可以写成这样,编译器直接报错了
// //释放
// delete p5;
// delete[] p4;
// return 0;
//}
//malloc与new的区别:malloc只申请空间,new包含申请空间+构造函数初始化
//释放空间时,free只释放空间,delete包含释放空间还调用了析构函数
class A {
public:
A(int a = 0)
:_a(0){
_a = a;
cout << "A" << endl;
}
~A() {
cout << "~A" << endl;
}
private:
int _a;
};
int main() {
int* p1 = new int;
int* p2 = (int*)malloc(sizeof(int));
A* p3 = (A*)malloc(sizeof(A));
A* p4 = new A;
//释放
free(p3);
delete p4;
return 0;
}
operator new与operator delete函数
申请资源:
1,operator new ==> 底层调用 malloc+失败抛出异常:bad allocation
2,new ==> operator new + 构造函数
3,new 比起malloc不一样的地方:1.调用构造函数初始化,2.失败了抛出异常
4,delete与free不一样的地方:delete会调用析构函数清理资源
5,operator delete和free:作用相似:都是只释放原始内存,不调用析构;但不是完全等同:operator delete 是 C++ 标准库函数,可以被重载;free 是 C 库函数,不能重载。底层默认 operator delete 内部确实调用 free,但语法、可扩展性不一样,不能说完全没区别。
举例:
java
class A {
public:
//构造函数
A(const int& a = 0)
:_a(a){
cout << "A()" << endl;
}
~A() {
cout << "~A()" << endl;
}
private:
int _a;
};
int main() {
A* p1 = (A*)malloc(sizeof(A));//只分配内存,不调用构造函数,控制台 不会输出 A()
//A* p2 = new A;
A* p3 = (A*)operator new(sizeof(A));//底层调用 malloc 分配内存,只分配内存,不调用构造函数,失败会抛异常,这里大小正常,不会抛
//operator new 和malloc的区别是什么?
//结果:使用方式都是一样的,处理错误的方式不一样
//为什么会出现下面的结果?这是一个值得思考的问题?
void* p4 = malloc(1024 * 1024 * 1024 *1);//0166B040
cout << p4 << endl;
void* p5 = malloc(1024 * 1024 * 1024 *2);//00000000
cout << p5 << endl;
void* p6 = malloc(1024 * 1024 * 1024 *3);//00000000
cout << p6 << endl;
void* p7 = malloc(1024 * 1024 * 1024 *4);//012F1900
cout << p7 << endl;
size_t size = 2;
void* p8 = malloc(size * 1024 * 1024 * 1024);//2G
cout << p8 << endl;//00000000,失败返回0
//void* p9 = operator new(size * 1024 * 1024 * 1024);
//cout << p9 << endl;//失败抛出异常
try {
void* p9 = operator new(size * 1024 * 1024 * 1024);
cout << p9 << endl;
}
catch (exception& e) {
cout << e.what() << endl;//bad allocation
}
return 0;
}
用operator new申请空间,我们想要调用构造函数时的调用方法
java
//使用operator new申请空间,我们想要调用构造函数时的调用方法
//typedef unsinged int size_t;
class A {
public:
//构造函数
A(const int& a = 0)
:_a(a) {
cout << "A()" << endl;
}
~A() {
cout << "~A()" << endl;
}
private:
int _a;
};
int main() {
A* p10 = (A*)operator new (sizeof(A));
new(p10)A(10);
p10->~A();
operator delete(p10);
return 0;
}

注意:
new /operator new /malloc 最终都是去 堆 (heap) 上申请内存
malloc 和 new 的区别
1.初始化与构造:new 会调用构造函数完成对象初始化;malloc仅分配内存,不会调用构造函数。
2.失败处理:new 内存分配失败时抛出异常;malloc 失败时返回 0
3.本质不同:malloc 是 C 标准库提供的库函数;new 是 C++ 内置操作符(关键字)。
4.使用方式:malloc 需要手动指定分配的字节数,返回值是无类型的 void*,必须强转才能使用;new 后面直接跟数据类型 / 对象,无需计算字节数,返回值是对应类型的指针,无需强转。
5.配套释放:new 分配的内存必须用delete释放(会调用析构函数);malloc 分配的内存必须用free释放。
内存泄漏
定义:
程序中已经不再使用的动态分配内存,因为忘记释放、释放逻辑错误等原因,导致内存一直被占用,就是内存泄漏。
c/c++与Java的区别:
内存泄漏是所有手动管理内存的语言(C/C++) 都存在的问题,不是 C++ 独有的。
Java 等语言:拥有自动垃圾回收机制(GC),对象不再被引用时会自动回收内存,不需要手动释放,几乎不会出现内存泄漏。
内存泄漏的危害
-
长期运行的服务 / 程序(如服务器、后台程序),泄漏会导致可用内存越来越少,最终程序运行变慢、卡顿甚至崩溃。
-
内存资源有限的设备(嵌入式、单片机、移动端),少量泄漏也会快速耗尽内存,导致程序异常。