繁体   English   中英

C++ 变量以及它们在 memory 中的存储位置(堆栈、堆、静态)

[英]C++ variables and where they are stored in memory (stack, heap, static)

我刚刚开始学习 C++,我想了解创建变量的不同方法以及不同关键字的含义。 我找不到任何真正经历过它的描述,所以我写了这篇文章以试图了解发生了什么。 我错过了什么吗? 我有什么错吗?

全局变量

全局变量既不存储在堆上,也不存储在堆栈上。 static全局变量是非导出的(标准全局变量可以用extern访问, static全局变量不能)

动态变量

使用指针访问的任何变量都存储在堆上。 堆变量使用new关键字分配,该关键字返回指向堆上 memory 地址的指针。 指针本身是一个标准堆栈变量。

{} 中未使用 new 创建的变量

存储在堆栈中,它的大小有限,因此只能用于基元和小型数据结构。 static关键字意味着该变量本质上是全局的,并且与全局变量存储在相同的 memory 空间中,但 scope 仅限于此函数/类。 const关键字意味着您不能更改变量。 thread_local类似于static但每个线程都有自己的变量。

登记

可以将变量声明为register以向编译器提示它应该存储在寄存器中。 编译器可能会忽略这一点,并将其应用于它认为最好的改进。 典型的用法是将索引或指针用作循环中的插入器。

好习惯

  1. 适用时默认使用const ,它更快。
  2. 警惕多线程应用程序中的static和全局变量,而不是使用thread_local或 mutex
  3. 在迭代器上使用register

笔记

在 function(非全局)中创建的任何不是staticthread_local且不是使用new创建的变量都将在堆栈中。 栈变量在memory中不要超过几KB,否则使用new放到堆上。

完整的可用系统 memory 可用于具有static关键字、 thread_local关键字、使用new或 global 创建的变量。

使用new创建的变量需要使用delete释放。 所有其他人在超出 scope 时都会自动释放,除了staticthead_local和 globals 之外,它们会在程序结束时释放。

尽管关于不应该如何使用全局变量的所有鹦鹉学舌,但不要被欺负:它们非常适合某些用例,并且比在堆上分配的变量更有效。 需要互斥锁以避免多线程应用程序中的竞争条件。

大部分是对的。

使用指针访问的任何变量都存储在堆上。

不是真的。 您可以拥有指向基于堆栈或全局变量的指针。

另外值得指出的是,全局变量通常由 linker 统一(即如果两个模块在全局 scope 处具有“ int i ”,那么您将只有一个名为“ i ”的全局变量)。 动态库稍微复杂了一点。 on Windows, DLLs don't have that behaviour (ie an " int i " in a Windows DLL will not be the same " int i " as in another DLL in the same process, or as the main executable), while most other platforms动态库可以 Darwin (iOS/macOS) 有一些额外的复杂性,它具有符号的分层命名空间; 只要您使用 flat_namespace 选项进行链接,我刚才所说的内容就会成立。

此外,值得一提的是初始化行为; 全局变量由运行时自动初始化(通常使用特殊的 linker 功能或通过插入到main函数代码中的调用)。 无法保证全局变量的初始化顺序。 However , static variables declared at function scope are initialised when that function is first executed, and not at program start-up as you might suppose, and that feature is commonly used by C++ programmers to do lazy initialisation.

(类似的问题适用于全局对象的析构函数;最好完全避免 IMO,尤其是因为在某些平台上存在根本不会调用它们的快速终止功能。)

const 关键字意味着您不能更改变量。

几乎。 const影响类型,并且根据您确切编写它的位置而有所不同。 例如

const char *foo;

应该读作foo是指向const char的指针,即foo本身不是const ,但它指向的东西是。 对比

char * const foo;

这表示foo是指向charconst指针。

最后,您错过了volatile ,其目的是告诉编译器不要对它适用的事物做出假设(例如,它不能假设在寄存器中缓存 volatile 值是安全的,或者优化访问,或者通常优化影响易失性值的任何操作)。 希望你永远不需要使用volatile 如果您正在做非常低级的事情,坦率地说很多人不需要 go 附近的任何地方,它通常很有用。

另一个答案是正确的,但没有提到使用register

编译器可能会忽略这一点,并将其应用于它认为最好的改进。

这是对的。 编译器非常擅长选择放入寄存器的变量(而典型的程序员不擅长这一点),以至于 C++ 委员会认为它完全没用。

此关键字在 C++11 中已弃用,并在 C++17 中删除(但仍保留以备将来使用)。

不要使用它。

您需要区分规范和实现。 该规范没有说明堆栈和堆,因为这是一个实现细节。 他们故意谈论Storage duration

如何实现此存储持续时间取决于目标环境以及编译器是否需要进行分配,或者这些值是否可以在编译时确定,然后只是机器代码的一部分(这肯定也是在内存的某些部分)。

因此,您的大多数描述都是For the target platform XY it will generally allocate on stack/heap if I do XY

C++也可以用作解释性语言,例如可以使用完全不同的方式处理 memory 的 cling。

它可以被交叉编译成某种字节解释器,其中每种类型都是动态分配的。

而对于嵌入式系统,memory 的管理/处理方式可能会更加不同。

堆变量使用new关键字分配,该关键字返回指向堆上 memory 地址的指针。

如果默认operator new, operator new[]映射到malloc (或给定操作系统中的任何其他等效项),则很可能是这种情况(如果确实需要分配 object)。

但是对于嵌入式系统,可能根本没有实现operator new, operator new[] The "OS" just might provide you a chunk of memory for the application that is handled like stack memory for which you manually reserve a certain amount of memory, and you implement a operator new and operator new[] that works with this preallocated memory,所以在这种情况下,你只有stack memory。

除此之外,您可以为某些类创建一个自定义operator new ,该运算符在某些与操作系统提供的“常规”memory 不同的硬件上分配 memory。

std::vector 将 memory 分配在 new 分配它的相同 memory 空间中,即堆,还是不是? 这很重要,因为它改变了我使用它的方式。

一个std::vector被定义为template<class T, class Allocator = std::allocator<T>> class vector; 所以有一个默认行为(由实现给出),其中向量分配 memory,对于常见的桌面操作系统,它使用像malloc这样的操作系统调用来动态分配 ZCD69B4957F06CD818D7BF3D61980E291 但是您也可以在任何其他可寻址的 memory 位置(例如堆栈)提供使用 memory 的自定义分配器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM