内存分配/释放?
我最近一直在研究内存分配问题,我对基础知识有点困惑。 我一直无法围绕简单的东西包扎头脑。 分配内存意味着什么? 怎么了? 我会很高兴回答以下任何问题:
如果有人能够回答这些C ++代码中malloc的含义,它也会帮助我:
char* x;
x = (char*) malloc (8);
谢谢。
记忆模型
C ++标准有一个内存模型。 它试图以通用的方式在计算机系统中对内存进行建模。 该标准定义了一个字节是存储器模型中的一个存储单元,该存储器由字节组成(§1.7):
C ++内存模型中的基本存储单元是字节。 [...] C ++程序可用的内存由一个或多个连续字节序列组成。
对象模型
标准总是提供一个对象模型。 这指定对象是一个存储区域(所以它由字节组成并驻留在内存中)(§1.8):
C ++程序中的结构创建,销毁,引用,访问和操作对象。 一个对象是一个存储区域。
所以我们走了。 内存是存储对象的地方。 要将对象存储在内存中,必须分配必需的存储区域。
分配和释放函数
该标准提供了两个隐式声明的全局范围分配函数:
void* operator new(std::size_t);
void* operator new[](std::size_t);
这些如何实施并不是标准的问题。 重要的是,它们应该返回一个指向某个存储区域的指针,其中传递的参数对应的字节数(§3.7.4.1):
分配函数尝试分配请求的存储量。 如果成功,它应该返回一个存储块的开始地址,这个存储块的长度以字节为单位,应至少与请求的大小一样大。 从分配函数返回时,分配存储的内容没有限制。
它还定义了两个相应的释放函数:
void operator delete(void*);
void operator delete[](void*);
定义哪些是用于释放先前已分配的存储空间(第3.7.4.2节):
如果在标准库中赋予释放函数的参数是一个不是空指针值(4.10)的指针,解除分配函数应该释放指针引用的存储器,使所有指向解除分配存储器任何部分的指针无效。
new
并delete
通常,您不应该直接使用分配和释放函数,因为它们只会给您提供未初始化的内存。 相反,在C ++中,您应该使用new
和delete
来动态分配对象。 新表达式通过使用上述分配函数之一获取请求类型的存储,然后以某种方式初始化该对象。 例如, new int()
将为int
对象分配空间,然后将其初始化为0.请参阅第5.3.4节:
新表达式通过调用分配函数(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标准库: malloc
和free
另外, <cstdlib>
头文件<cstdlib>
了stdlib.h
C标准库的内容,其中包含malloc
和free
函数。 它们也被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
, malloc
或realloc
函数返回的指针不匹配,或者如果通过调用free
或realloc
释放空间,则行为未定义。
但是,在C ++中使用malloc
和free
是没有好的借口的。 如前所述,C ++有其自己的选择。
问题的答案
所以要直接回答你的问题:
正在分配的“记忆”在哪里?
C ++标准不关心。 它只是说程序有一些由字节组成的内存。 该内存可以分配。
这是什么“记忆”? 数组中的空间? 或者是其他东西?
就标准而言,内存只是一个字节序列。 这是有目的的非常通用的,因为该标准只尝试对典型的计算机系统建模。 大多数情况下,您可以将其视为计算机RAM的模型。
这个“内存”被分配时究竟发生了什么?
分配内存使某些存储区域可供程序使用。 对象在分配的内存中初始化。 所有你需要知道的是你可以分配内存。 物理内存的实际分配往往是由操作系统完成的。
当内存被释放时究竟发生了什么?
取消分配先前分配的内存会导致该内存对程序不可用。 它成为释放存储。
如果有人能够回答这些C ++代码中malloc的含义,它也会帮助我:
char* x;
x = (char*) malloc (8);
在这里, malloc
只是分配8个字节的内存。 它返回的指针正被转换为char*
并存储在x
。
1)正在分配的“内存”在哪里?
根据你的操作系统,编程环境(gcc vs Visual C ++ vs Borland c ++ vs其他),计算机,可用内存等等,这是完全不同的。一般来说,内存是从所谓的堆,内存区域等待分配的周围供你使用。 它通常会使用您的可用RAM。 但总有例外。 大多数情况下,只要它给我们记忆,它的来源不是一个很大的问题。 有特殊类型的内存,例如虚拟内存,在任何给定时间可能实际上可能不在RAM中,并且如果您用完实际内存,可能会将其移动到硬盘驱动器(或类似的存储设备)。 完整的解释将会很长!
2)这个“记忆”是什么? 数组中的空间? 或者是其他东西?
内存通常是计算机中的内存。 如果将内存视为一个巨大的“数组”是有帮助的,它肯定会像一个一样操作,然后将其视为大量字节(8位值,非常类似于unsigned char
值)。 它从内存底部的索引0开始。 然而,就像以前一样,这里有很多例外情况,某些内存部分可能会映射到硬件,甚至根本不存在!
3)当这个“内存”被分配时会发生什么?
在任何时候应该有(我们真的希望!)其中一些可用于软件分配。 它如何分配是高度依赖于系统的。 一般来说,分配一个区域的内存,分配器将其标记为已使用,然后指定给您使用,指示程序在您系统的所有内存中的内存所在的位置。 在你的例子中,程序会找到一个8字节的连续块(char),并返回一个指向它标识为“正在使用”之后发现该块的位置的指针。
4)当内存被释放时究竟发生了什么?
系统将该内存标记为可供再次使用。 这非常复杂,因为这往往会在内存中造成漏洞。 分配8个字节,然后再分配8个字节,然后释放前8个字节,你就有了一个漏洞。 有关于处理释放,内存分配等方面的全部书籍。所以希望简短的回答就足够了!
5)如果有人能够回答这些C ++语言中malloc的含义,它也能真正帮助我:
真的很粗鲁,并假设它在一个函数中(顺便说一句,从不这样做,因为它不会释放你的内存并导致内存泄漏):
void mysample() {
char *x; // 1
x = (char *) malloc(8); // 2
}
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
实际的分配在第3点中有详细的描述.Marcoc通常只是调用一个系统函数来处理所有其他事情,并且像这里的其他所有内容一样,它与操作系统,操作系统,系统等等有很大不同。
1。 正在分配的“记忆”在哪里?
从语言角度来看,这并没有具体说明,主要是因为细节往往无关紧要。 另外, C++
标准在指定硬件细节方面往往会出错,以尽量减少不必要的限制(平台编译器都可以运行,并且可能进行优化)。
sftrabbit的回答给出了关于这个结局的很好的概述(并且这是你真正需要的),但是我可以给出几个有用的例子以防万一。
例1:
在一台足够旧的单用户计算机上(或者一个足够小的嵌入式计算机),大多数物理RAM可以直接用于你的程序。 在这种情况下,调用malloc
或new
基本上是内部簿记,允许运行时库跟踪那些RAM当前正在使用的块。 您可以手动执行此操作,但它很快就会变得乏味。
例2:
在现代多任务操作系统上,物理RAM与许多进程和其他任务(包括内核线程)共享。 它也用于后台的磁盘缓存和I / O缓冲,并且通过虚拟内存子系统进行扩展,当不使用时可以将数据交换到磁盘(或某些其他存储设备)。
在这种情况下,调用new
可能会首先检查你的进程是否已经有足够的空间可用,并且如果没有请求更多的操作系统。 无论返回什么内存,都可能是物理的,或者它可能是虚拟的(在这种情况下,物理RAM可能不会被分配来存储它,直到它实际被访问)。 至少在不使用特定于平台的API的情况下,您甚至无法区分这种差异,因为内存硬件和内核合谋将其隐藏起来。
2。 这是什么“记忆”? 数组中的空间? 或者是其他东西?
在例1中,它就像数组中的空间一样:返回的地址标识一个可寻址的物理RAM块。 即使在这里,RAM地址也不一定是平坦的或连续的 - 有些地址可能保留给ROM或I / O端口。
在例2中,它是一个更虚拟的索引:你的进程的地址空间。 这是一个抽象,用于隐藏进程中的底层虚拟内存细节。 当你访问这个地址时,内存硬件可能直接访问一些真实的RAM,或者它可能需要让虚拟内存子系统提供一些。
3。 这个“内存”被分配时究竟发生了什么?
通常,返回一个指针,您可以使用它来存储您请求的字节数。 在这两种情况下, malloc
或new
操作员都会进行一些内务管理,以跟踪进程地址空间的哪些部分被使用,哪些是免费的。
4。 当内存被释放时究竟发生了什么?
一般来说, free
或delete
将做一些家务管理,以便他们知道内存可以重新分配。
如果有人能够回答这些C ++代码中malloc的含义,它也会帮助我:
char* x;
x = (char*) malloc (8);
它返回一个指针,它可以是NULL
(如果它找不到所需的8个字节),或者是一些非NULL值。
对这个非NULL值唯一有用的说法是:
x[0]..x[7]
每一个都是合法的(也是安全的), 0 <= i <= 7
否则访问x[-1]
或x[8]
或实际上任何x[i]
都是非法的(未定义行为) x, x+1, ..., x+8
任何一个都是合法的(尽管你不能解除其中的最后一个) x
符合它们 上一篇: Memory Allocation/Deallocation?
下一篇: Why is initialising a matrix whose size is a power of 2 slow?