内存分配/解除分配?

Isa*_*aac 36 c++ memory memory-management allocation

我最近一直关注内存分配,对基础知识我有点困惑.我无法绕过简单的东西.分配内存意味着什么?怎么了?我很感激这些问题的答案:

  1. 正在分配的"记忆"在哪里?
  2. 这个"记忆"是什么?阵列中的空间?或者是其他东西?
  3. 当这个"记忆"被分配时会发生什么?
  4. 当内存被解除分配时会发生什么?
  5. 如果有人可以回答malloc在这些C++行中所做的事情,它也会对我有所帮助:

    char* x; 
    x = (char*) malloc (8);
    
    Run Code Online (Sandbox Code Playgroud)

谢谢.

Jos*_*eld 60

记忆模型

C++标准有一个内存模型.它试图以通用方式对计算机系统中的存储器进行建模.该标准定义了一个字节是内存模型中的存储单元,该内存由字节组成(§1.7):

C++内存模型中的基本存储单元是字节.[...] C++程序可用的内存由一个或多个连续字节序列组成.

对象模型

标准始终提供对象模型.这指定对象是存储区域(因此它由字节组成并驻留在内存中)(§1.8):

C++程序中的构造创建,销毁,引用,访问和操作对象.对象是存储区域.

我们去了.内存是存储对象的地方.要将对象存储在内存中,必须分配所需的存储区域.

分配和释放功能

该标准提供了两个隐式声明的全局范围分配函数:

void* operator new(std::size_t);
void* operator new[](std::size_t);
Run Code Online (Sandbox Code Playgroud)

如何实施这些并不是标准的关注点.重要的是它们应该返回一个指向某个存储区域的指针,其中包含与传递的参数相对应的字节数(§3.7.4.1):

分配功能尝试分配所请求的存储量.如果成功,它将返回存储块的起始地址,其长度以字节为单位应至少与请求的大小一样大.从分配函数返回时,分配的存储的内容没有限制.

它还定义了两个相应的释放函数:

void operator delete(void*);
void operator delete[](void*);
Run Code Online (Sandbox Code Playgroud)

其中定义了解除分配先前已分配的存储(第3.7.4.2节):

如果给标准库中的释放函数赋予的参数是一个不是空指针值的指针(4.10),则释放函数将释放指针引用的存储,使所有指向解除分配存储的任何部分的指针无效. .

newdelete

通常,您不需要直接使用分配和释放函数,因为它们只会为您提供未初始化的内存.相反,在C++中,您应该使用newdelete动态分配对象.甲新表达通过使用上述分配功能中的一个,然后初始化以某种方式,对象获得针对所请求的类型的存储.例如,new int()将为int对象分配空间,然后将其初始化为0.参见§5.3.4:

new-expression通过调用分配函数(3.7.4.1)获取对象的存储空间.

[...]

新表达式创建类型T的对象初始化该对象[...]

在相反的方向,delete将调用对象的析构函数(如果有),然后释放存储(第5.3.5节):

如果delete-expression的操作数的值不是空指针值,则delete-expression将调用该对象的析构函数(如果有)或要删除的数组的元素.

[...]

如果delete-expression的操作数的值不是空指针值,则delete-expression将调用释放函数(3.7.4.2).

其他分配

但是,这些并不是分配或取消分配存储的唯一方法.该语言的许多结构隐式地需要分配存储.例如,给出一个对象定义int a;,也需要存储(第7节):

定义导致保留适当的存储量并进行任何适当的初始化(8.5).

C标准库:mallocfree

此外,<cstdlib>标题引入了stdlib.hC标准库的内容,其中包括mallocfree函数.它们也由C标准定义为分配和释放内存,就像C++标准定义的分配和释放函数一样.这是malloc(C99§7.20.3.3)的定义:

void *malloc(size_t size);
说明
malloc函数为大小由其指定size且其值不确定的对象分配空间.
返回
malloc函数返回空指针或指向已分配空间的指针.

free(C99§7.20.3.2)的定义:

void free(void *ptr);
说明
free函数使得指向的空间ptr被释放,即可用于进一步分配.如果ptr是空指针,则不执行任何操作.否则,如果该参数不匹配的指针早些时候返回calloc,mallocrealloc功能,或如果空间已释放通过调用free或者realloc,该行为是不确定的.

然而,从未有一个很好的借口,是使用mallocfree++用C.如前所述,C++有自己的替代品.


问题的答案

所以直接回答你的问题:

  1. 正在分配的"记忆"在哪里?

    C++标准并不关心.它只是说程序有一些由字节组成的内存.可以分配该内存.

  2. 这个"记忆"是什么?阵列中的空间?或者是其他东西?

    就标准而言,内存只是一个字节序列.这是有目的的非常通用,因为标准只试图模拟典型的计算机系统.在大多数情况下,您可以将其视为计算机RAM的模型.

  3. 当这个"记忆"被分配时会发生什么?

    分配内存使得某些存储区域可供程序使用.对象在分配的内存中初始化.您需要知道的是,您可以分配内存.实际为您的进程分配物理内存往往由操作系统完成.

  4. 当内存被解除分配时会发生什么?

    取消分配一些先前分配的内存会导致该内存对程序不可用.它变成了解除分配的存储空间.

  5. 如果有人可以回答malloc在这些C++行中所做的事情,它也会对我有所帮助:

    char* x; 
    x = (char*) malloc (8);
    
    Run Code Online (Sandbox Code Playgroud)

    这里malloc只是分配8个字节的内存.它返回的指针被强制转换为a char*并存储在其中x.

  • @Isaac如果您创建局部变量或使用`new`和`delete`动态分配对象,则根本不必关心分配.编译器将确保分配适当的存储量.类类型通常在成员之间包含填充字节,但它们有用.就标准而言,你不应该*需要*来关心这些东西.但是,实际上,您可能需要.有关SO的一些重要问题与此相关([此处](http://stackoverflow.com/q/8547778/150634),[此处](http://stackoverflow.com/q/12264970/150634),等等.) (2认同)

Mar*_*ton 11

1)正在分配的"记忆"在哪里?

这完全不同,基于您的操作系统,编程环境(gcc vs Visual C++ vs Borland C++ vs其他任何东西),计算机,可用内存等.通常,内存是从所谓的堆分配的,内存区域只是等待你可以使用.它通常会使用您可用的RAM.但总有例外.在大多数情况下,只要它给了我们记忆,它来自哪里并不是一个很大的问题.存在特殊类型的存储器,例如虚拟存储器,其在任何给定时间实际上可能或可能不在RAM中,并且如果用尽实际存储器,则可能移动到硬盘驱动器(或类似的存储设备).完整的解释会很长!

2)这个"记忆"是什么?阵列中的空间?或者是其他东西?

内存通常是计算机中的RAM.如果将内存视为一个巨大的"数组"是有帮助的,它肯定会像一个一样运行,然后将其视为一个字节(8位值,非常类似于unsigned char值).它从内存底部的索引0开始.就像之前一样,这里有很多例外,内存的某些部分可能映射到硬件,甚至可能根本不存在!

3)当这个"记忆"被分配时会发生什么?

在任何给定的时间应该(我们真的希望!)其中一些可供软件分配.如何分配是高度依赖系统的.通常,分配一个内存区域,分配器将其标记为已使用,然后指定给您使用它指示程序在内存所在的所有系统内存中的位置.在您的示例中,程序将找到一个8字节(char)的连续块,并返回一个指针,指向它在将其标记为"正在使用"之后发现该块的位置.

4)当内存被解除分配时会发生什么?

系统将该内存标记为可再次使用.这非常复杂,因为这通常会在内存中造成漏洞.分配8个字节,然后再分配8个字节,然后解除分配前8个字节,你就有了一个漏洞.有关于处理释放,内存分配等的全书,所以希望简短的回答就足够了!

5)如果有人可以回答malloc在这些C++行中所做的事情,它也会对我有所帮助:

非常粗暴,并假设它在一个函数中(顺便说一句,从来没有这样做,因为它不会释放你的内存并导致内存泄漏):

void mysample() {
  char *x; // 1
  x = (char *) malloc(8); // 2
}
Run Code Online (Sandbox Code Playgroud)

1)这是在本地堆栈空间中保留的指针.它没有被初始化,因此它指向内存中的任何内存.

2)它使用参数8调用malloc.演员只是让C/C++知道你打算将它作为(char*),因为它返回一个(void*)意味着它没有应用任何类型.然后生成的指针存储在x变量中.

在非常粗糙的x86 32位组件中,这看起来很模糊

PROC mysample:
  ; char *x;
  x = DWord Ptr [ebp - 4]
  enter 4, 0   ; Enter and preserve 4 bytes for use with 

  ; x = (char *) malloc(8);
  push 8       ; We're using 8 for Malloc
  call malloc  ; Call malloc to do it's thing
  sub esp, 4   ; Correct the stack
  mov x, eax   ; Store the return value, which is in EAX, into x

  leave
  ret
Run Code Online (Sandbox Code Playgroud)

在第3点中模糊地描述了实际的分配.Malloc通常只调用一个处理所有其余部分的系统函数,就像这里的其他所有内容一样,它从操作系统到操作系统,系统到系统等都有很大的不同.


Use*_*ess 7

1.正在分配的"记忆"在哪里?

从语言的角度来看,这没有指定,主要是因为细节通常无关紧要.此外,C++标准倾向于在指定硬件细节不足的情况下犯错,以最小化不必要的限制(在编译器可以运行的平台上以及可能的优化).

sftrabbit的回答很好地概述了这一结果(这是你真正需要的),但我可以提供一些有用的例子以防万一.

例1:

在足够旧的单用户计算机(或足够小的嵌入式计算机)上,大多数物理RAM可以直接用于您的程序.在这种情况下,调用mallocnew基本上是内部簿记,允许运行时库跟踪当前正在使用的RAM的哪些块.您可以手动执行此操作,但很快就会变得乏味.

例2:

在现代多任务操作系统上,物理RAM与许多进程和其他任务共享,包括内核线程.它还用于后台的磁盘缓存和I/O缓冲,并由虚拟内存子系统增强,该子系统可以在不使用时将数据交换到磁盘(或其他存储设备).

在这种情况下,调用new可能首先检查您的进程是否已在内部有足够的空间,如果没有,则从操作系统请求更多.无论返回什么内存都可以是物理的,或者它可以是虚拟的(在这种情况下,可能不会分配物理RAM来存储它直到它实际被访问).你甚至无法区分,至少在没有使用特定于平台的API的情况下,因为内存硬件和内核会共同隐藏它.

2.这个"记忆"是什么?阵列中的空间?或者是其他东西?

在示例1中,它类似于数组中的空格:返回的地址标识可寻址的物理RAM块.即使在这里,RAM地址也不一定是平坦的或连续的 - 某些地址可能被保留用于ROM或I/O端口.

在示例2中,它是一个更虚拟的索引:您的流程的地址空间.这是一个抽象,用于隐藏进程中的底层虚拟内存详细信息.当您访问此地址时,内存硬件可能会直接访问某些实际RAM,或者可能需要请求虚拟内存子系统提供一些内存.

3.当这个"记忆"被分配时会发生什么?

通常,会返回一个指针,您可以使用该指针存储所需的字节数.在这两种情况下,malloc或者new操作员都会做一些内务处理来跟踪过程的地址空间的哪些部分被使用以及哪些部分是免费的.

4.当内存被解除分配时会发生什么?

一般来说,freedelete将做一些内务管理,以便他们知道可以重新分配内存.

如果有人可以回答malloc在这些C++行中所做的事情,它也会对我有所帮助:

char* x; 
x = (char*) malloc (8);
Run Code Online (Sandbox Code Playgroud)

它返回一个指针NULL(如果它找不到你想要的8个字节),或者一些非NULL值.

关于这个非NULL值,你可以有用的唯一的事情是:

  • 访问这8个字节中的每个字节是合法的(并且是安全的)x[0]..x[7],
  • 这是非法的(未定义行为)来访问x[-1]x[8]或实际上任何 x[i]除非0 <= i <= 7
  • 比较任何一个是合法的x, x+1, ..., x+8(尽管你不能取消引用最后一个)
  • 如果你的平台/硬件/对你在内存中存储数据的位置有任何限制,那么就x满足它们