简体   繁体   English

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

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

I've just begun learning C++ and I wanted to get my head around the different ways to create variables and what the different keywords mean.我刚刚开始学习 C++,我想了解创建变量的不同方法以及不同关键字的含义。 I couldn't find any description that really went through it, so I wrote this to try to understand what's going on.我找不到任何真正经历过它的描述,所以我写了这篇文章以试图了解发生了什么。 Have I missed anything?我错过了什么吗? Am I wrong about anything?我有什么错吗?

Global Variables全局变量

Global variables are stored neither on the heap nor stack.全局变量既不存储在堆上,也不存储在堆栈上。 static global variables are non-exported (standard global variables can be accessed with extern , static globals cannot) static全局变量是非导出的(标准全局变量可以用extern访问, static全局变量不能)

Dynamic Variables动态变量

Any variable that is accessed with a pointer is stored on the heap.使用指针访问的任何变量都存储在堆上。 Heap variables are allocated with the new keyword, which returns a pointer to the memory address on the heap.堆变量使用new关键字分配,该关键字返回指向堆上 memory 地址的指针。 The pointer itself is a standard stack variable.指针本身是一个标准堆栈变量。

Variables inside {} that aren't created with new {} 中未使用 new 创建的变量

Stored in the stack, which is limited in size so it should be used only for primitives and small data structures.存储在堆栈中,它的大小有限,因此只能用于基元和小型数据结构。 static keyword means the variable is essentially global and stored in the same memory space as global variables, but scope is restricted to this function/class. static关键字意味着该变量本质上是全局的,并且与全局变量存储在相同的 memory 空间中,但 scope 仅限于此函数/类。 const keyword means you can't change the variable. const关键字意味着您不能更改变量。 thread_local is like static but each thread gets its own variable. thread_local类似于static但每个线程都有自己的变量。

Register登记

A variable can be declared as register to hint to the compiler that it should be stored in the register.可以将变量声明为register以向编译器提示它应该存储在寄存器中。 The compiler will probaby ignore this and apply it to whatever it thinks would be the best improvement.编译器可能会忽略这一点,并将其应用于它认为最好的改进。 Typical usage would be for an index or pointer being used as an interator in a loop.典型的用法是将索引或指针用作循环中的插入器。

Good practice好习惯

  1. Use const by default when applicable, its faster.适用时默认使用const ,它更快。
  2. Be wary of static and globals in multithreaded applications, instead use thread_local or mutex警惕多线程应用程序中的static和全局变量,而不是使用thread_local或 mutex
  3. Use register on iterators在迭代器上使用register

Notes笔记

Any variables created inside a function (non-global) that is not static or thread_local and is not created with new , will be on the stack.在 function(非全局)中创建的任何不是staticthread_local且不是使用new创建的变量都将在堆栈中。 Stack variables should not exceed more than a few KB in memory, otherwise use new to put it on the heap.栈变量在memory中不要超过几KB,否则使用new放到堆上。

The full available system memory can be used for variables with static keyword, thread_local keyword, created with new , or global.完整的可用系统 memory 可用于具有static关键字、 thread_local关键字、使用new或 global 创建的变量。

Variables created with new need to be freed with delete .使用new创建的变量需要使用delete释放。 All others are automatically freed when they're out of scope, except static , thead_local and globals which are freed when the program ends.所有其他人在超出 scope 时都会自动释放,除了staticthead_local和 globals 之外,它们会在程序结束时释放。

Despite all the parroting about how globals should not be used, don't be bullied: they are great for some use cases, and more efficient than variables allocated on the heap.尽管关于不应该如何使用全局变量的所有鹦鹉学舌,但不要被欺负:它们非常适合某些用例,并且比在堆上分配的变量更有效。 Mutexes will be needed to avoid race conditions in multi-threaded applications.需要互斥锁以避免多线程应用程序中的竞争条件。

Mostly right.大部分是对的。

Any variable that is accessed with a pointer is stored on the heap.使用指针访问的任何变量都存储在堆上。

This isn't true.不是真的。 You can have pointers to stack-based or global variables.您可以拥有指向基于堆栈或全局变量的指针。

Also it's worth pointing out that global variables are generally unified by the linker (ie if two modules have " int i " at global scope, you'll only have one global variable called " i ").另外值得指出的是,全局变量通常由 linker 统一(即如果两个模块在全局 scope 处具有“ int i ”,那么您将只有一个名为“ i ”的全局变量)。 Dynamic libraries complicate that slightly;动态库稍微复杂了一点。 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 dynamic libraries do . 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动态库可以 There are some additional complications on Darwin (iOS/macOS) which has a hierarchical namespace for symbols; Darwin (iOS/macOS) 有一些额外的复杂性,它具有符号的分层命名空间; as long as you're linking with the flat_namespace option, what I just said will hold.只要您使用 flat_namespace 选项进行链接,我刚才所说的内容就会成立。

Additionally, it's worth talking about initialisation behaviour;此外,值得一提的是初始化行为; global variables are initialised automatically by the runtime (typically either using special linker features or by means of a call that is inserted into the code for your main function).全局变量由运行时自动初始化(通常使用特殊的 linker 功能或通过插入到main函数代码中的调用)。 The order of initialisation of globals isn't guaranteed.无法保证全局变量的初始化顺序。 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. 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.

(Similar concerns apply to destructors for global objects; those are best avoided entirely IMO, not least because on some platforms there are fast termination features that simply won't call them.) (类似的问题适用于全局对象的析构函数;最好完全避免 IMO,尤其是因为在某些平台上存在根本不会调用它们的快速终止功能。)

const keyword means you can't change the variable. const 关键字意味着您不能更改变量。

Almost.几乎。 const affects the type, and there is a difference depending on where you write it exactly. const影响类型,并且根据您确切编写它的位置而有所不同。 For example例如

const char *foo;

should be read as foo is a pointer to a const char , ie foo itself is not const , but the thing it points at is.应该读作foo是指向const char的指针,即foo本身不是const ,但它指向的东西是。 Contrast with对比

char * const foo;

which says that foo is a const pointer to char .这表示foo是指向charconst指针。

Finally, you've missed out volatile , the point of which is to tell the compiler not to make assumptions about the thing to which it applies (eg it can't assume that it's safe to cache a volatile value in a register, or to optimise away accesses, or in general to optimise across any operation that affects a volatile value).最后,您错过了volatile ,其目的是告诉编译器不要对它适用的事物做出假设(例如,它不能假设在寄存器中缓存 volatile 值是安全的,或者优化访问,或者通常优化影响易失性值的任何操作)。 Hopefully you'll never need to use volatile ;希望你永远不需要使用volatile it's most often useful if you're doing really low-level things that frankly a lot of people have no need to go anywhere near.如果您正在做非常低级的事情,坦率地说很多人不需要 go 附近的任何地方,它通常很有用。

The other answer is correct, but doesn't mention the use of register .另一个答案是正确的,但没有提到使用register

The compiler will probaby ignore this and apply it to whatever it thinks would be the best improvement.编译器可能会忽略这一点,并将其应用于它认为最好的改进。

This is correct.这是对的。 Compilers are so good at choosing variables to put in registers (and typical programmer is bad at that), that C++ committees decided it's completely useless.编译器非常擅长选择放入寄存器的变量(而典型的程序员不擅长这一点),以至于 C++ 委员会认为它完全没用。

This keyword was deprecated in C++11 and removed in C++17 (but it's still reserved for possible future use).此关键字在 C++11 中已弃用,并在 C++17 中删除(但仍保留以备将来使用)。

Do not use it.不要使用它。

You need to differentiate between specification and implementation.您需要区分规范和实现。 The specification does not say anything about stack and heap, because that's an implementation detail.该规范没有说明堆栈和堆,因为这是一个实现细节。 They purposely talk about Storage duration .他们故意谈论Storage duration

How this storage duration is achieved depends on the target environment and if the compiler needs to do allocations those at all or if these values can be determined at the compile-time, and are then only part of the machine code (which for sure is also at some part of the memory).如何实现此存储持续时间取决于目标环境以及编译器是否需要进行分配,或者这些值是否可以在编译时确定,然后只是机器代码的一部分(这肯定也是在内存的某些部分)。

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

C++ could also be used as an interpreted language eg cling that could have completely different ways of handling memory. C++也可以用作解释性语言,例如可以使用完全不同的方式处理 memory 的 cling。

It could be cross-compiled to some kind of byte interpreter in which every type is dynamically allocated.它可以被交叉编译成某种字节解释器,其中每种类型都是动态分配的。

And when it comes to embedded systems the way how memory is managed/handled might be even more different.而对于嵌入式系统,memory 的管理/处理方式可能会更加不同。

Heap variables are allocated with the new keyword, which returns a pointer to the memory address on the heap.堆变量使用new关键字分配,该关键字返回指向堆上 memory 地址的指针。

If the defaultoperator new, operator new[] are mapped to something like malloc (or any other equivalent in the given OS) this is likely the case (if the object really needs to be allocated).如果默认operator new, operator new[]映射到malloc (或给定操作系统中的任何其他等效项),则很可能是这种情况(如果确实需要分配 object)。

But for embedded systems, it might be the case that operator new, operator new[] aren't implemented at all.但是对于嵌入式系统,可能根本没有实现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, so in such a case you only have stack memory. 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。

Besides that, you can create a custom operator new for certain classes that allocates the memory on some hardware that is different to the "regular" memory provided by the OS.除此之外,您可以为某些类创建一个自定义operator new ,该运算符在某些与操作系统提供的“常规”memory 不同的硬件上分配 memory。

The std::vector is allocating the memory in the same memory space that new is allocating it, ie the heap, or its not? std::vector 将 memory 分配在 new 分配它的相同 memory 空间中,即堆,还是不是? This is important because it changes how I use it.这很重要,因为它改变了我使用它的方式。

A std::vector is defined as template<class T, class Allocator = std::allocator<T>> class vector;一个std::vector被定义为template<class T, class Allocator = std::allocator<T>> class vector; so there is a default behavior (that is given by the implementation) where the vector allocates memory, for common Desktop OS it uses something like OS call like malloc to dynamically allocate memory.所以有一个默认行为(由实现给出),其中向量分配 memory,对于常见的桌面操作系统,它使用像malloc这样的操作系统调用来动态分配 ZCD69B4957F06CD818D7BF3D61980E291 But you could also provide a custom allocator that uses memory at any other addressable memory location (eg stack).但是您也可以在任何其他可寻址的 memory 位置(例如堆栈)提供使用 memory 的自定义分配器。

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

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