最近我一直在研究内存分配,但对基础知识还有些困惑。我还没有完全理解其中的简单概念。什么是内存分配?会发生什么?我希望能得到以下任何问题的答案:
- "内存"分配在哪里?
- 这个"内存"是什么?数组中的空间吗?还是其他东西?
- 当这个"内存"被分配时会发生什么?
- 当内存被释放时会发生什么?
如果有人可以回答下面这些C++代码中malloc的作用,那将对我很有帮助:
char* x;
x = (char*) malloc (8);
谢谢。
最近我一直在研究内存分配,但对基础知识还有些困惑。我还没有完全理解其中的简单概念。什么是内存分配?会发生什么?我希望能得到以下任何问题的答案:
如果有人可以回答下面这些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);
它还定义了两个相应的释放函数:分配函数尝试分配请求的存储量。如果成功,它应该返回一个指向存储块起始地址的指针,其长度(以字节为单位)至少与请求大小一样大。从分配函数返回时,分配的存储内容无任何限制。
void operator delete(void*);
void operator delete[](void*);
以下函数被定义为释放之前已分配的存储空间 (§3.7.4.2):
如果标准库中的 deallocation 函数所接收的参数是一个非空指针 (4.10),则该 deallocation 函数必须释放由指针引用的存储空间,并使所有指向已释放存储空间的任何部分的指针无效。
new
和 delete
通常情况下,您不需要直接使用分配和释放函数,因为它们只会给您未初始化的内存。相反,在 C++ 中,您应该使用 new
和 delete
来动态地分配对象。一个 new-expression 通过使用上述分配函数之一来获取所请求类型的存储空间,然后以某种方式初始化该对象。例如,new int()
将分配一个 int
对象的空间,然后将其初始化为 0。参见 §5.3.4:
新表达式通过调用分配函数 (3.7.4.1) 获得对象的存储空间。
[...]
创建类型 T 的对象的 new 表达式初始化该对象 [...]
相反,delete
将调用对象的析构函数(如果有)然后释放存储空间 (§5.3.5):
如果 delete-expression 的操作数的值不是空指针值,则 delete-expression 将调用正在删除的对象或数组元素的析构函数(如果有)。
[...]
如果 delete-expression 的操作数的值不是空指针值,则 delete-expression 将调用 deallocation 函数 (3.7.4.2)。
然而,这些并不是分配或释放存储空间的唯一方式。许多语言结构隐式要求分配存储空间。例如,给出一个对象定义,如 int a;
,也需要存储空间 (§7):
定义会导致适当数量的存储空间被保留,并进行任何适当的初始化 (8.5)。
malloc
和 free
此外,<cstdlib>
头文件引入了 stdlib.h
C 标准库的内容,其中包括 malloc
和 free
函数。它们也被 C 标准定义为分配和释放内存,类似于 C++ 标准定义的分配和释放函数。这是 malloc
的定义 (C99 §7.20.3.3):
void *malloc(size_t size);
描述
malloc
函数分配指定大小的对象空间,其值不确定。
返回值
malloc
函数返回空指针或分配空间的指针。
free
的定义(C99 §7.20.3.2):
void free(void *ptr);
描述
free
函数释放指向的空间,使其可以被重新分配。如果ptr
是空指针,则不执行任何操作。否则,如果参数与之前由calloc、malloc或realloc函数返回的指针不匹配,或者空间已经被free
或realloc调用释放,则行为未定义。
然而,在C++中使用malloc
和free
没有充分的理由,因为C++有自己的替代方法。
直接回答您的问题:
Where is the "memory" that is being allocated?
The C++ standard doesn't care. It simply says that the program has some memory which is made up of bytes. This memory can be allocated.
What is this "memory"? Space in an array? Or something else?
As far as the standard is concerned, the memory is just a sequence of bytes. This is purposefully very generic, as the standard only tries to model typical computer systems. You can, for the most part, think of it as a model of the RAM of your computer.
What happens exactly when this "memory" gets allocated?
Allocating memory makes some region of storage available for use by the program. Objects are initialized in allocated memory. All you need to know is that you can allocate memory. The actual allocation of physical memory to your process tends to be done by the operating system.
What happens exactly when the memory gets deallocated?
Deallocating some previously allocated memory causes that memory to be unavailable to the program. It becomes deallocated storage.
It would also really help me if someone could answer what malloc does in these C++ lines:
char* x;
x = (char*) malloc (8);
Here, malloc
is simply allocating 8 bytes of memory. The pointer it returns is being cast to a char*
and stored in x
.
new
和delete
动态分配对象,则无需关心分配问题。编译器将确保分配正确数量的存储空间。类类型通常在成员之间包含填充字节,但它们具有一定目的。就标准而言,您不应该需要关心这些内容。然而,在实践中,您可能需要关注这些内容。一些与此相关的最热门的SO问题(这里,这里,等等)。 - Joseph Mansfield1) "memory"被分配在哪里?
这完全取决于你的操作系统、编程环境(gcc vs Visual C++ vs Borland C++ vs 其他任何工具)、计算机及可用内存等因素。一般情况下,内存是从堆中进行分配的,堆是一个等待你使用的内存区域。它通常会使用可用的RAM。但总会有例外情况。就大多数情况而言,只要分配内存,它来自哪里并不是一件很重要的事情。还有一些特殊类型的内存,例如虚拟内存,可能实际上并没有在任何时候存在于RAM中,并且如果你的真实内存不足,则可以将其移动到硬盘(或类似的存储设备)中。详细的解释需要写很长!
2) 这个"memory"是什么?数组中的空间吗?还是其他东西?
内存通常是您计算机中的RAM。如果将内存视为一个巨大的“数组”,它确实像一个数组一样运行,那么请将其视为大量字节(8位值,类似于unsigned char值)。它从内存底部的索引0开始。但是同样地,在这里有大量的例外情况,一些内存部分可能映射到硬件,或者根本不存在!
3) "memory"被分配时会发生什么?
在任何时候,软件都应该有一些可供分配的内存(我们真的希望如此!)。它是如何分配的高度依赖于系统。通常情况下,分配一个内存区域,分配器将其标记为已使用,然后给您返回一个指针,告诉程序Memory所在的所有系统内存中哪里。在您的示例中,程序将查找8个字节(char)的连续块并在将其标记为“正在使用”后返回它找到该块的位置的指针。
4) "memory"被释放时会发生什么?
系统将该内存标记为可用以再次使用。这非常复杂,因为这通常会导致内存中的空洞。分配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
实际分配通常在第三点模糊地描述。 Malloc通常只是调用处理所有其余内容的系统函数,就像这里的所有其他内容一样,它在不同的操作系统、系统上有很大不同等。
1 . 被分配的“内存”存储在哪里?
从语言角度来看,这并没有具体说明,主要是因为细节问题通常并不重要。此外,C++
标准倾向于低估硬件细节,以最小化不必要的限制(对编译器可以运行的平台和可能的优化)。
sftrabbit的答案给出了一个很好的概述(这就是你需要的所有内容),但我可以举几个实际例子,以帮助你理解。
在足够老的单用户计算机上(或足够小的嵌入式系统中),大部分物理 RAM 可能直接可供程序使用。在这种情况下,调用 malloc
或 new
实质上是内部簿记,允许运行时库跟踪当前正在使用的该 RAM 块。你也可以手动做到这一点,但很快就会变得乏味。
在现代多任务操作系统上,物理 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]
是合法的(和安全的);x[-1]
或x[8]
或者实际上任何x[i]
除非0 <= i <= 7
是不合法的(未定义行为);x, x+1, ..., x+8
都是合法的(尽管你不能解引用最后一个);x
符合这些限制。