doc/docs/CPlusPlus-main/17_C++的内存模型.ipynb
sairate fa9377e4ae docs(book): 添加现代 C++教程及相关代码
- 新增现代 C++ 教程的 Preface 章节,包括英文和中文版本
- 添加 C++ Primer 练习代码
- 新增 Learn C++ 教程的 C++ 开发简介章节
- 添加头文件解析文档
- 更新 mkdocs.yml,包含新教程的目录结构
- 修改项目设置,使用 Python 3.10环境
2025-07-08 09:52:45 +08:00

430 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# C++的内存模型"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 1. 内存分区"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"① C++在程序执行时将内存大方向划分为4个区域。\n",
"\n",
"1. 代码区:存放函数体的二进制代码,由操作系统进行管理的。\n",
"2. 全局区:存放全局变量和静态变量以及常量。\n",
"3. 栈区:由编译器自动分配释放,存放函数的参数值、局部变量等。\n",
"4. 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。\n",
"\n",
"② 内存四区的意义:不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"① 在程序编译后生成了exe可执行程序未执行该程序前分为两个区域代码区、全局区。\n",
"\n",
"② 所有的写的代码(注释、变量、语句等)都会放到代码区中。\n",
"\n",
"③ 栈区中的数据由编译器决定数据的生成和死亡。\n",
"\n",
"④ 堆区中的数据由程序员决定数据的生存和死亡。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 2. 内存四区"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2.1 代码区"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"① 代码区:\n",
"\n",
"1. 存放CPU执行的机器指令。\n",
"2. 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要内存中有一份代码即可。\n",
"3. 代码区是只读的,使其只读的原因是防止程序员意外地修改了它的指令。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2.2 全局区"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"① 全局区:\n",
"\n",
"1. 全局变量和静态变量存放在此,局部常量不放在全局区。\n",
"2. 全局区还包含了常量区,字符串常量和其他常量也存放在此。\n",
"3. 该区域的数据在程序结束后由操作系统释放。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#include <iostream>\n",
"using namespace std;\n",
"\n",
"//全局变量\n",
"int g_a = 10;\n",
"int g_b = 20;\n",
"\n",
"//const修饰的全局常量\n",
"const int c_g_a = 50;\n",
"const int c_g_b = 60;\n",
"\n",
"int main()\n",
"{\n",
" //全局区\n",
"\n",
" //全局变量、静态变量、常量\n",
"\n",
" //创建普通局部变量\n",
" int a = 30;\n",
" int b = 40;\n",
"\n",
" cout << \"局部变量a的地址为\" << (int)&a << endl;\n",
" cout << \"局部变量b的地址为\" << (int)&b << endl;\n",
"\n",
" cout << \"全局变量g_a的地址为\" << (int)&g_a << endl;\n",
" cout << \"全局变量g_b的地址为\" << (int)&g_b << endl;\n",
"\n",
" //静态变量 在普通变量前面加static,属于静态变量\n",
" static int s_a = 10;\n",
" static int s_b = 10;\n",
"\n",
" cout << \"静态变量s_a的地址为\" << (int)&s_a << endl;\n",
" cout << \"静态变量s_b的地址为\" << (int)&s_b << endl;\n",
"\n",
" //常量\n",
" //字符串常量\n",
" cout << \"字符串常量的地址为:\" << (int)&\"hello world\" << endl;\n",
"\n",
" //const修饰的变量\n",
" //const修饰的局部变量,const修饰的局部变量\n",
" cout << \"全局常量c_g_a的地址为\" << (int)&c_g_a << endl;\n",
" cout << \"全局常量c_g_a的地址为\" << (int)&c_g_b << endl;\n",
"\n",
" const int c_l_a = 10; // c-const g-global l-local;\n",
" const int c_l_b = 10;\n",
" cout << \"局域常量c_l_a的地址为\" << (int)&c_l_a << endl;\n",
" cout << \"局域常量c_l_b的地址为\" << (int)&c_l_a << endl;\n",
"\n",
" system(\"pause\");\n",
"\n",
" return 0;\n",
"\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"运行结果: \n",
" - 局部变量a的地址为14023100 \n",
" - 局部变量b的地址为14023088 \n",
" - 全局变量g_a的地址为4964352 \n",
" - 全局变量g_b的地址为4964356 \n",
" - 静态变量s_a的地址为4964360 \n",
" - 静态变量s_b的地址为4964364 \n",
" - 字符串常量的地址为4955128 \n",
" - 全局常量c_g_a的地址为4954928 \n",
" - 全局常量c_g_a的地址为4954932 \n",
" - 局域常量c_l_a的地址为14023076 \n",
" - 局域常量c_l_b的地址为14023076 \n",
" - 请按任意键继续. . ."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2.3 栈区"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"① 栈区\n",
"\n",
"1. 由编译器自动分配释放,存放函数的参数值、局部变量等。\n",
"2. 不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#include <iostream>\n",
"using namespace std;\n",
"\n",
"//栈区数据注意事项:不要返回局部变量的地址\n",
"//栈区的数据由编译器管理开辟和释放\n",
"\n",
"int* func(int b) //返回的是一个地址 int*;形参的数据也会放在栈区\n",
"{\n",
" int a = 10; //局部变量存放的数据10存放在栈区栈区的数据在函数执行完后自动释放\n",
" return &a; //返回局部变量的地址即数据10的的地址\n",
"}\n",
"\n",
"int main()\n",
"{\n",
" //接收func函数的返回值\n",
" int* p = func(1); //用指针接收栈区上的数据10的地址由于栈区上数据10已经被释放所以对地址解引用会获得乱码的值。\n",
"\n",
" cout << *p << endl; //第一次可以 打印正确的数字,是因为编译器做了保留\n",
" cout << *p << endl; //第二次这个数据就不在保留了。\n",
"\n",
" system(\"pause\");\n",
"\n",
" return 0;\n",
"\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"运行结果: \n",
" - 10 \n",
" - 1905325 \n",
" - 请按任意键继续. . ."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2.4 堆区"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"① 堆区:\n",
"\n",
"1. 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。\n",
"2. 在C++中主要利用new在堆区开辟内存。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#include <iostream>\n",
"using namespace std;\n",
"\n",
"int* func() //返回的是一个地址 int*;形参的数据也会放在栈区\n",
"{\n",
" //利用new关键字可以将数据开辟到堆区\n",
" //指针本质也是变量,这里的指针是局部变量,局部变量的数据放在栈上,即指针保存的数据是放在堆区\n",
" int * p = new int(10); //new关键字会返回一个地址因此用栈上的指针来接收堆上数据的地址。\n",
" return p;\n",
"}\n",
"int main()\n",
"{\n",
" //在堆区开辟数据\n",
" int* p = func(); //堆区的地址返回给 *p 了,栈区数据是否,堆区数据没释放\n",
"\n",
" cout << *p << endl;\n",
" cout << *p << endl;\n",
" cout << *p << endl;\n",
" cout << *p << endl;\n",
"\n",
" system(\"pause\");\n",
"\n",
" return 0;\n",
"\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"运行结果: \n",
" - 10 \n",
" - 10 \n",
" - 10 \n",
" - 10 \n",
" - 请按任意键继续. . ."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 3. new操作符"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"① C++中利用new操作符在堆区开辟数据。\n",
"\n",
"② 堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符 delete。\n",
"\n",
"③ 语法new 数据类型\n",
"\n",
"④ 利用new创建的数据会返回该数据对应的类型的指针。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#include <iostream>\n",
"using namespace std;\n",
"\n",
"//1、new的基本语法\n",
"\n",
"int* func() //返回的是一个地址 int*;形参的数据也会放在栈区\n",
"{\n",
" //在堆区创建整型数据\n",
" //new返回是该数据类型的指针\n",
" int* p = new int(10);\n",
" return p;\n",
"}\n",
"\n",
"void test01()\n",
"{\n",
" int* p = func();\n",
" cout << *p << endl;\n",
" cout << *p << endl;\n",
" cout << *p << endl;\n",
" //堆区的数据,由程序员管理开辟,程序员管理释放\n",
" //如果想释放堆区数据,利用关键字 delete\n",
" delete p;\n",
"\n",
" // cout << *p << endl; //报错,内存已经被释放,再次访问就是非法操作,会报错\n",
"}\n",
"\n",
"//2、在堆区利用new开辟数据\n",
"void test02()\n",
"{\n",
" //创建10整型数据的数组在堆区\n",
" int* arr = new int[10];//10代表数组有10个元素\n",
"\n",
" for (int i = 0; i < 10; i++)\n",
" {\n",
" arr[i] = i + 100; //给10个元素赋值 100~109\n",
" }\n",
"\n",
" for (int i = 0; i < 10; i++)\n",
" {\n",
" cout << arr[i] << endl; \n",
" }\n",
" //释放堆区数据\n",
" //释放数组的时候,要加[]才可以\n",
" delete[] arr;\n",
"}\n",
"\n",
"int main()\n",
"{\n",
" test01();\n",
" test02();\n",
"\n",
" system(\"pause\");\n",
"\n",
" return 0;\n",
"\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"运行结果: \n",
" - 10 \n",
" - 10 \n",
" - 10 \n",
" - 100 \n",
" - 101 \n",
" - 102 \n",
" - 103 \n",
" - 104 \n",
" - 105 \n",
" - 106 \n",
" - 107 \n",
" - 108 \n",
" - 109 \n",
" - 请按任意键继续. . ."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.6.3",
"language": "python",
"name": "python3.6.3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.3"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": false,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
}
},
"nbformat": 4,
"nbformat_minor": 4
}