{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/c-memory-model/",
  "atlas": "https://www.pystone.net/?node=c-memory-model#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/c-memory-model.md",
  "context": "https://www.pystone.net/notes/c-memory-model.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:c-memory-model",
  "slug": "c-memory-model",
  "title": "C语言内存模型",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "C语言内存模型",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "022a2a5abb01862183e0f30dcdc9571d440e8cb5b53f33a93d517895adb3fc39",
  "assets": [
    {
      "reference": "assets/0020 - C语言内存模型__Image.png",
      "url": "/media/5f75f9332608bd8d937d.png",
      "mediaType": "image/png",
      "contentHash": "5f75f9332608bd8d937de1a36ae380eebdf356b4eb9f084db7ec8e8ff9b878cf",
      "byteLength": 31169,
      "width": 706,
      "height": 591
    },
    {
      "reference": "assets/702px-Windows_2000_architecture.svg.png",
      "url": "/media/11b2e47b8f69068879cb.png",
      "mediaType": "image/png",
      "contentHash": "11b2e47b8f69068879cb31406847eb8ff10b0bc5e51e1ce2a0b5829cc2e2ffe3",
      "byteLength": 69798,
      "width": 702,
      "height": 900
    }
  ],
  "headings": [
    {
      "depth": 1,
      "text": "C语言内存模型",
      "anchor": "c语言内存模型",
      "citation": "https://www.pystone.net/notes/c-memory-model/#c%E8%AF%AD%E8%A8%80%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B"
    },
    {
      "depth": 2,
      "text": "C语言内存布局概述",
      "anchor": "c语言内存布局概述",
      "citation": "https://www.pystone.net/notes/c-memory-model/#c%E8%AF%AD%E8%A8%80%E5%86%85%E5%AD%98%E5%B8%83%E5%B1%80%E6%A6%82%E8%BF%B0"
    },
    {
      "depth": 3,
      "text": "内存区域总结",
      "anchor": "内存区域总结",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F%E6%80%BB%E7%BB%93"
    },
    {
      "depth": 3,
      "text": "C语言编辑运行机制与内存",
      "anchor": "c语言编辑运行机制与内存",
      "citation": "https://www.pystone.net/notes/c-memory-model/#c%E8%AF%AD%E8%A8%80%E7%BC%96%E8%BE%91%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6%E4%B8%8E%E5%86%85%E5%AD%98"
    },
    {
      "depth": 3,
      "text": "延申知识",
      "anchor": "延申知识",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%BB%B6%E7%94%B3%E7%9F%A5%E8%AF%86"
    },
    {
      "depth": 4,
      "text": "PE文件格式",
      "anchor": "pe文件格式",
      "citation": "https://www.pystone.net/notes/c-memory-model/#pe%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F"
    },
    {
      "depth": 4,
      "text": "可执行文件、目标文件、DLL",
      "anchor": "可执行文件目标文件dll",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%8F%AF%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6dll"
    },
    {
      "depth": 4,
      "text": "Windows NT",
      "anchor": "windows-nt",
      "citation": "https://www.pystone.net/notes/c-memory-model/#windows-nt"
    },
    {
      "depth": 2,
      "text": "三种内存分配方式对比",
      "anchor": "三种内存分配方式对比",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E4%B8%89%E7%A7%8D%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E6%96%B9%E5%BC%8F%E5%AF%B9%E6%AF%94"
    },
    {
      "depth": 4,
      "text": "从静态存储区分配",
      "anchor": "从静态存储区分配",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E4%BB%8E%E9%9D%99%E6%80%81%E5%AD%98%E5%82%A8%E5%8C%BA%E5%88%86%E9%85%8D"
    },
    {
      "depth": 4,
      "text": "在栈上创建",
      "anchor": "在栈上创建",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%9C%A8%E6%A0%88%E4%B8%8A%E5%88%9B%E5%BB%BA"
    },
    {
      "depth": 4,
      "text": "从堆上分配（动态内存分配）",
      "anchor": "从堆上分配动态内存分配",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E4%BB%8E%E5%A0%86%E4%B8%8A%E5%88%86%E9%85%8D%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D"
    },
    {
      "depth": 2,
      "text": "关于堆区和栈区",
      "anchor": "关于堆区和栈区",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%85%B3%E4%BA%8E%E5%A0%86%E5%8C%BA%E5%92%8C%E6%A0%88%E5%8C%BA"
    },
    {
      "depth": 3,
      "text": "堆和栈的区别",
      "anchor": "堆和栈的区别",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%A0%86%E5%92%8C%E6%A0%88%E7%9A%84%E5%8C%BA%E5%88%AB"
    },
    {
      "depth": 3,
      "text": "关于结构体或类的内存分配",
      "anchor": "关于结构体或类的内存分配",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%85%B3%E4%BA%8E%E7%BB%93%E6%9E%84%E4%BD%93%E6%88%96%E7%B1%BB%E7%9A%84%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D"
    },
    {
      "depth": 4,
      "text": "结构体的成员",
      "anchor": "结构体的成员",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E7%BB%93%E6%9E%84%E4%BD%93%E7%9A%84%E6%88%90%E5%91%98"
    },
    {
      "depth": 2,
      "text": "内存对齐（memory alignment）",
      "anchor": "内存对齐memory-alignment",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90memory-alignment"
    },
    {
      "depth": 2,
      "text": "函数调用的过程——函数栈",
      "anchor": "函数调用的过程函数栈",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E7%9A%84%E8%BF%87%E7%A8%8B%E5%87%BD%E6%95%B0%E6%A0%88"
    },
    {
      "depth": 2,
      "text": "经典的堆栈的讲解",
      "anchor": "经典的堆栈的讲解",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E7%BB%8F%E5%85%B8%E7%9A%84%E5%A0%86%E6%A0%88%E7%9A%84%E8%AE%B2%E8%A7%A3"
    },
    {
      "depth": 2,
      "text": "相关语法",
      "anchor": "相关语法",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E7%9B%B8%E5%85%B3%E8%AF%AD%E6%B3%95"
    },
    {
      "depth": 3,
      "text": "new和malloc的区别",
      "anchor": "new和malloc的区别",
      "citation": "https://www.pystone.net/notes/c-memory-model/#new%E5%92%8Cmalloc%E7%9A%84%E5%8C%BA%E5%88%AB"
    },
    {
      "depth": 3,
      "text": "关于free",
      "anchor": "关于free",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E5%85%B3%E4%BA%8Efree"
    },
    {
      "depth": 3,
      "text": "例子",
      "anchor": "例子",
      "citation": "https://www.pystone.net/notes/c-memory-model/#%E4%BE%8B%E5%AD%90"
    },
    {
      "depth": 2,
      "text": "Ref",
      "anchor": "ref",
      "citation": "https://www.pystone.net/notes/c-memory-model/#ref"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:programming-languages-and-runtimes",
      "title": "编程语言与运行时",
      "url": "https://www.pystone.net/notes/programming-languages-and-runtimes/",
      "atlas": "https://www.pystone.net/?node=programming-languages-and-runtimes#knowledge-atlas",
      "label": "编程语言与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "C++中的“C语言内存模型”导航项",
      "citation": "https://www.pystone.net/notes/programming-languages-and-runtimes/#c"
    }
  ],
  "contentMarkdown": "# C语言内存模型\n\n> 创建时间：2020/10/6 18:54\n\n  * C语言内存模型\n    * C语言内存布局概述\n      * 内存区域总结\n      * C语言编辑运行机制与内存\n      * 延申知识\n        * PE文件格式\n        * 可执行文件、目标文件、DLL\n        * Windows NT\n    * 三种内存分配方式对比\n      *         * 从静态存储区分配\n        * 在栈上创建\n        * 从堆上分配（动态内存分配）\n    * 关于堆区和栈区\n      * 堆和栈的区别\n      * 关于结构体或类的内存分配\n        * 结构体的成员\n    * 内存对齐（memory alignment）\n    * 函数调用的过程——函数栈\n    * 经典的堆栈的讲解\n    * 相关语法\n      * new和malloc的区别\n      * 关于free\n      * 例子\n    * Ref\n\n## C语言内存布局概述\n\n### 内存区域总结\n\n![Alt text](assets/0020 - C语言内存模型__Image.png)\n\n栈区（stack）：由编译器自动分配与释放，存放为运行时函数分配的局部变量、函数参数、返回数据、返回地址等。其操作类似于数据结构中的栈。\n堆区（heap）：一般由程序员自动分配，如果程序员没有释放，程序结束时可能有OS回收。其分配类似于链表。\n全局/静态存储区 （static .bss段和.data段）：存放全局变量、静态数据、常量。程序结束后由系统释放。全局区分为已初始化全局区（data）和未初始化全局区（bss）。\n常量区（.rodata段）：存放常量字符串，程序结束后有系统释放。\n代码区（.text段）：存放函数体（类成员函数和全局区）的二进制代码。\n\n### C语言编辑运行机制与内存\n\n在Windows平台下，高级语言写出的程序经过编译链接，最终会变成PE文件。当PE文件被装载运行后，就成了所谓的进程。\nPE文件代码段中包含的二进制级别的机器代码会被装入内存的 **代码区(.text)** ，处理器将到内存的这个区域一条一条地取出指令和操作数，并送入运算逻辑单元进行运算；如果代码中请求开辟 **动态内存** ，则会在内存的 **堆区** 分配一块大小合适的区域返回给代码区的代码使用；当函数调用发生时， **函数的调用关系等信息** 会动态地保存在内存的 **栈区** ，以供处理器在执行完被调用函数的代码时，返回母函数。\n\n### 延申知识\n\n#### PE文件格式\n\n**PE文件** （Portable Executable，可移植性可执行文件）是一种用于 **可执行文件** 、 **目标文件** 和 **动态链接库** 的文件格式，主要使用在32位和64位的Windows操作系统上。\n\n> “可移植的”是指该文件格式的通用性，可用于许多种不同的操作系统和体系结构中。\n\nPE文件格式封装了Windows操作系统加载可执行程序代码时所必需的一些信息。这些信息包括动态链接库、API导入和导出表、资源管理数据和线程局部存储数据。在Windows NT操作系统中，PE文件格式主要用于EXE文件、DLL文件、.sys（驱动程序）和其他文件类型。\n\n#### 可执行文件、目标文件、DLL\n\n**可执行文件** 指一种内容可 **被电脑解释为程序** 的电脑文件。通常可执行文件内，含有 **以二进制编码的微处理器指令** ，也因此可执行文件有时称为 **二进制档** 。\n\n* * *\n\n**目标文件** （object file）即存放目标代码的计算机文件，它常被称作 **二进制文件（binaries）** 。\n**目标代码** （英语：object code）指计算机科学中编译器或汇编器处理源代码后所生成的代码，它一般由机器代码或接近于机器语言的代码组成。\n\n目标文件是从 **源代码文件** 产生 **程序文件** 这一过程的 **中间产物** ， **链接器** 正是通过把目标文件链接在一起来生成可执行文件或库文件。\n\n> 目标文件包含着机器代码（可直接被计算机中央处理器执行）以及代码在运行时使用的数据，如重定位信息，如用于链接或调试的程序符号（变量和函数的名字），此外还包括其他调试信息。\n\n* * *\n\n**DLL** (动态链接库, Dynamic-link library) 是微软公司在Windows操作系统中实现 **共享函数库** 概念的一种实现方式。这些库函数的扩展名是.DLL、.OCX（包含ActiveX控制的库）或者.DRV（旧式的系统驱动程序）。\n\n#### Windows NT\n\n**Windows NT（Windows New Technology）** 是美国微软公司1993年推出的纯32位操作系统 **核心** 。\nWindows NT 3.1 ~ Windows 2000 ~ Windows XP ~ Windows Vista ~ Windows 10\n\n![Windows 2000 architecture](assets/702px-Windows_2000_architecture.svg.png)\n\n## 三种内存分配方式对比\n\n#### 从静态存储区分配\n\n内存在程序编译的时候已经分配好，这块内存在程序的整个运行期间都存在。例如全局变量，static变量。\n\n#### 在栈上创建\n\n在执行函数时，函数内局部变量的存储单元可以在栈上创建，函数执行结束时，这些内存单元会自动被释放。\n栈内存分配运算内置于处理器的指令集，效率高，但是分配的内存容量有限。\n\n#### 从堆上分配（动态内存分配）\n\n程序在运行的时候使用malloc或者new申请任意多少的内存，程序员自己负责在何时用free或delete释放内存。\n动态内存的生命周期有程序员决定，使用非常灵活，但如果在堆上分配了空间，既有责任回收它，否则运行的程序会出现内存泄漏，频繁的分配和释放不同大小的堆空间将会产生内存碎片。\n\n## 关于堆区和栈区\n\n栈区（stack）用于存放局部变量、返回地址、函数的实参等，由编译器自动分配释放。栈的功能主要是函数调用、局部变量申请、函数参数传递所使用的空间，是为函数调用的实现提供一些保存、恢复操作。一般情况下栈空间都特别小。\n在windows下，栈地址是向低地址扩展的数据结构，是一块连续的内存区域。栈顶的地址和栈的最大容量是系统预先规定好的，在windows下，栈的大小是2MB，而申请堆空间的大小一般小于2GB\n栈存放的数据占空间比较小，而且栈内存不由程序员来管理，存放在栈里面的局部变量随着函数调用结束就自动释放了。\n\n堆区（heap）是一个程序运行动态分配的内存区域。一般由程序员分配释放，若程序员不释放，程序结束时可能由OS回收。 程序员通过new等方式创建的对象，一般会在堆区中分配内存。\n\n我们平常说的GC，指的是对堆内存的回收。对内存的优化一般也指的是堆内存。\n\n### 堆和栈的区别\n\n**管理方式** 不同： **栈** 是由 **编译器自动** 申请和释放空间， **堆** 是需要 **程序员手动** 申请和释放；\n**空间** 大小不同： **栈** 的空间是有限的，在32位平台下，VC6下默认为 **1M** ， **堆** 最大可以到 **4G** ；\n能否产生 **碎片** ： **栈** 和数据结构中的栈原理相同，在弹出一个元素之前，上一个已经弹出了， **不会** 产生碎片，如果不停地调用 **malloc、free堆** 造成内存 **碎片很多** ；\n**生长方向** 不同： **堆** 生长方向是 **向上** 的，也就是向着内存地址增加的方向， **栈** 刚好相反，向着内存 **减小的方向** 生长。\n**分配方式** 不同： **堆** 都是 **动态分配** 的，没有静态分配的堆。 **栈** 有 **静态** 分配和 **动态** 分配。 **静态** 分配是 **编译器完成** 的，比如局部变量的分配。 **动态分配** 由 **malloc 函数进行分配** ，但是栈的动态分配和堆是不同的，它的 **动态分配是由编译器进行释放** ，无需我们手工实现。\n**分配效率** 不同： **栈** 的效率比堆 **高** 很多。栈是机器系统提供的数据结构，计算机在底层提供栈的支持，分配专门的寄存器来存放栈的地址，压栈出栈都有相应的指令，因此比较快。 **堆** 是由库函数提供的，机制很复杂，库函数会按照一定的算法进行搜索内存，因此比较慢。\n\n栈区（stack）用于存放局部变量、返回地址、函数的实参等，由编译器自动分配释放。栈的功能主要是函数调用、局部变量申请、函数参数传递所使用的空间，是为函数调用的实现提供一些保存、恢复操作。一般情况下栈空间都特别小。\n在windows下，栈地址是向低地址扩展的数据结构，是一块连续的内存区域。栈顶的地址和栈的最大容量是系统预先规定好的，在windows下，栈的大小是2MB，而申请堆空间的大小一般小于2GB\n栈存放的数据占空间比较小，而且栈内存不由程序员来管理，存放在栈里面的局部变量随着函数调用结束就自动释放了。\n\n栈区大小的限制，不同的资料上有不同的说法，有个文章说申请大小的限制是1或者2M，可以自己改，但是最大不超过8M\n\n堆区（heap）是一个程序运行动态分配的内存区域。一般由程序员分配释放，若程序员不释放，程序结束时可能由OS回收。 程序员通过new等方式创建的对象，一般会在堆区中分配内存。\n\n我们平常说的GC，指的是对堆内存的回收。对内存的优化一般也指的是堆内存。\n\n嗯，这段描述基本适用于多数语言了。不过c# 函数里new的struct对象（值类型）一般情况都是在栈上的，和c++不一样。然后堆的空间在64位下就没有2G或者4G的限制了。\n\n### 关于结构体或类的内存分配\n\n```cpp\nstring str1 = &quot;test&quot;;\nstring str2 = &quot;test&quot;;\n\n```\n\ndog1在栈中\ndog2指向堆中的结构体\n\n  * 函数内部声明的局部变量的内存由编译器在栈中自动分配与释放，函数结束时栈会回收，局部变量也随之销毁。如果局部变量为类对象，其析构函数会被自动调用。\n\n  * 若使用new/malloc分配内存，则该内存在堆上，需要由程序员释放。\n如以下语句：\n\n```cpp\nstring str1 = &quot;test&quot;;\nstring str2 = &quot;test&quot;;\n\n```\n\n这行语句的含义是，使对象obj具有“自动存储（automatic storage）”的性质。所谓“自动存储”，意思是这个对象的存储位置取决于其声明所在的上下文。如果这个语句出现在函数内部，那么它就在栈上创建对象。如果这个语句不是在函数内部，而是作为一个类的成员变量，则取决于这个类的对象是如何分配的。\n\n#### 结构体的成员\n\n```cpp\nstring str1 = &quot;test&quot;;\nstring str2 = &quot;test&quot;;\n\n```\n\n使用方法如下：\n\n  * unit_t1申请了 **栈** 空间，不需要自己去考虑使用后释放空间的事情。\n\n  * unit_t2中申请了 **堆** 空间，需要自己释放。\n\n```cpp\nstring str1 = &quot;test&quot;;\nstring str2 = &quot;test&quot;;\n\n```\n\n## 内存对齐（memory alignment）\n\n操作的内存地址（更准确的说，操作内存的起始地址）能够被操作的内存大小整除，满足这个要求的内存访问叫做访问对齐的内存（aligned memory access），否则就是访问未对齐的内存（unaligned memory access）。\n<https://www.cnblogs.com/zhao-zongsheng/p/9099603.html>\n\n## 函数调用的过程——函数栈\n\n<https://blog.csdn.net/m0_37717595/article/details/80368411>\n\n## 经典的堆栈的讲解\n\n<https://blog.csdn.net/yingms/article/details/53188974>\n\n## 相关语法\n\n### new和malloc的区别\n\n  1. new 是 **运算符** malloc是 **库函数**\n\n  2. **new和delete底层还是由malloc和free实现** 的，区别在于new和delete在生成复杂数据类型时会执行两步: **分配内存** 和 **调用构造函数** / **调用析构函数** 和 **销毁内存** 。\n\n  3. **malloc** 申请的内存 **没有类型** ,为(void*) 需要根据使用需求进行强制转换, **new** 不需要,返回 **对应类型的指针** 。\n\n  4. new并不是每次都会调用构造函数，对于简单类型eg.int是不需要的。\n\n### 关于free\n\n>   * 当不需要再使用申请的内存时，记得 **释放** ，并 **且指针=NULL** ；释放后应该把指向这块内存的指针指向NULL， **防止程序后面不小心使用了它** 。\n>\n>   * free时候,指针不能发生偏移，原因是指针指向的是申请空间的首地址，而且申请的空间的大小在free源码结构体中有记录\n>\n>\n\n### 例子\n\n```cpp\nstring str1 = &quot;test&quot;;\nstring str2 = &quot;test&quot;;\n\n```\n\n## Ref\n\n<https://www.cnblogs.com/houjun/p/4909413.html>\n<https://www.cnblogs.com/qiuly/p/8576149.html>\n<http://www.360doc.com/content/12/0102/11/1299815_176623363.shtml>\n<https://www.zhihu.com/question/19729973>\n<http://www.360doc.com/content/12/0102/11/1299815_176623363.shtml>\n<https://blog.csdn.net/yingms/article/details/53188974>\n<https://www.cnblogs.com/cchHers/p/10010275.html>\n<https://zhuanlan.zhihu.com/p/78478567>\n<https://www.zhihu.com/question/59238963>\n<https://blog.csdn.net/cherrydreamsover/article/details/81627855>\n<https://blog.csdn.net/buptzy/article/details/80149622>\n<https://www.cnblogs.com/will-boot/p/3302936.html>\n<https://blog.csdn.net/feliciafay/article/details/9830005>\n"
}
