为什么分配内存?(C++)
Why allocate memory? (C++)
我正在努力理解C++中的内存分配。我想到的一个问题是,为什么分配内存如此必要?如果我们在不分配内存的情况下使用内存,会发生什么?此外,我还震惊地发现C++在内存分配方面是如此的粗心。If通过无边界检查的数组提供对内存的自由访问。
int main()
{
int *p = new int[5];
p[1] = 3;
p[11118] = 9;
cout<<p[11118]<<'n';
}
上面的代码工作,输出9。
在什么情况下,将值分配给未分配的内存位置是危险的?潜在的不良影响是什么?我正在访问的内存位置是否已分配给其他程序,并且为其赋值可能会导致该程序以非常意外的方式崩溃/行为?
以上代码为未定义行为。它可以工作,工作不正确,根本不工作,崩溃,或通过微软Skype订购披萨。你不应该依赖未定义的行为。
为什么必须分配内存因为这样,你就把记忆标记为你的。没有其他人可以使用它。它还验证是否有可用的内存。如果您的系统只有1000字节的内存,那么只选择1500字节来存储一些数据是个坏主意。
如果我们在不分配内存的情况下使用内存,会发生什么没人知道。你写的地址可能不存在。其他进程可能已经开始使用它,因此您覆盖了它们的数据。内存可以得到保护;例如,在前一种情况下,操作系统可能会注意到您正在访问另一个进程声称拥有的内存,并阻止您。您可能拥有该内存区域,但由于某种原因,程序的另一部分正在使用它,并且您已经覆盖了自己的数据。
通过无边界检查的数组自由访问内存该代码不起作用。。。目前,它的功能正如预期的那样,但那不是一回事。从形式上讲,这是未定义的行为,因此编译器可以发出代码来执行它想要的任何操作。
在什么情况下,将值分配给未分配的内存位置是危险的我举了一些上面的例子。也有可能打破你的堆栈。调用函数时,会存储函数应返回的地址。如果你通过不小心的内存访问覆盖了这个值,那么当你离开这个函数时,谁知道你会去哪里?也许那个利用你程序的人。。。一个常见的漏洞是将可执行代码加载到内存的某个部分,然后使用现有程序中的错误来运行它。有一次,在我正在使用的嵌入式设备上,我遇到了一个fencepost错误,导致我的函数返回到其他指令的中间。这本应该让我的芯片崩溃,但幸运的是,该指令的后半部分本身就是一条有效的指令。最终运行的代码序列使设备获得了感知能力,并最终完成了我们正在进行的项目。现在,它只是在我的地下室播放魔兽世界。这就是未定义行为的恐怖。
很多不错的答案,但我觉得在"为什么我们需要分配内存"方面缺少一些东西。我认为了解计算机程序的控制流如何在最低级别工作是很重要的,因为C和C++是硬件上相对较薄的抽象层。
虽然只使用if和goto就可以在一个巨大的全局范围内编写程序,但大多数真实世界的程序都被拆分为函数,这些函数是独立的、可移动的模块,可以随意调用。为了跟踪所有数据(参数、返回值、局部变量),所有这些数据都放在一个称为堆栈的一维连续内存区域中。调用一个函数会将放在堆栈上,从函数返回会弹出数据,下一个函数调用会覆盖相同的内存区域。
这样,所有函数代码都可以通过记住相对于其入口点的局部数据的偏移量来抽象存储,并且可以从许多不同的上下文中调用同一个函数——函数的局部变量可能位于不同的绝对地址,但它们相对于函数的入口地址始终处于相同的相对位置。
当函数被调用和返回时,堆栈内存会不断被覆盖,这意味着您不能将任何持久数据放在堆栈上,即放在局部变量中,因为函数返回后,局部变量的内存不会保持完整。如果您的函数需要将持久数据存储在某个地方,则它必须将该数据存储在其他地方。另一个位置是所谓的堆,您可以在其上手动(也称为"动态")通过malloc
或new
请求持久存储。该内存区域位于其他位置,不会被任何人回收或覆盖,您可以安全地将指向该内存的指针传递给周围的任何人。唯一的缺点是,除非你手动告诉系统你已经完成了,否则它将无法将内存用于其他任何事情,这就是为什么你必须手动清理这个动态分配的内存。但是,对存储持久信息的函数的需求是我们需要分配内存的原因。
(只是为了完成这幅图:堆栈上的局部变量被称为"自动分配"。还有"静态分配",它发生在编译时,也是全局变量所在的地方。如果你有一个全局char[30000]
,你可以很高兴地从程序中的任何地方读写它。)。)
在堆上分配内存允许动态分配具有动态生存期的动态内存量。
如果你想要边界检查,你可以通过std::vector::at()得到它。
在什么情况下,将值分配给未分配的内存位置是危险的?
所有情况。
潜在的不良影响是什么?
意外行为。
是否有可能我正在访问的内存位置已分配给其他程序,并且为其赋值可能会导致该程序以非常意外的方式崩溃/行为?
取决于操作系统。
这似乎有两个问题:
- 为什么c++不进行边界检查
- 为什么我们需要动态内存分配
我的答案:
- 因为那样会更慢。您总是可以编写一个检查边界的访问器函数,如std::vector::at()
- 因为不能在运行时调整内存大小可能会非常不方便(参见早期的FORTRAN)
在大多数操作系统中,主机中可用的物理内存和应用程序代码可以看到的逻辑内存占用之间存在明显的分隔。在大多数情况下,这是由CPU中一个称为内存管理单元(MMU)的部分来实现的,它有很多有用的目标。
最明显的是,它允许您为一个应用程序(或多个应用程序)分配比机器上实际存在的内存更多的内存。当应用程序要求从内存中获取一些数据时,MMU会调用操作系统来确定内存的实际位置,无论是在核心还是在磁盘上,如果内存已经被调出的话。
这样做的另一个用途是将一些地址分段用于应用程序使用之外的目的,例如,大多数计算机中的GPU通过CPU作为核心存储器可见的存储器区域来控制,并且它可以非常有效地读取或写入该存储器区域。MMU为操作系统提供了一种使用该内存的方式,但使其对正常应用程序不可访问。
由于这种分段以及其他原因,在向操作系统请求用于特定目的的一些内存之前,应用程序通常无法获得完整的地址范围。例如,在linux上,应用程序通过调用brk
或sbrk
来请求更多核心内存,并且通过调用mmap
来请求内存映射IO。在通过其中一个调用返回地址之前,该地址未映射,访问它将导致segfault,通常会终止有问题的程序。
有些平台只将内存暴露给它知道已经映射的应用程序,但C++在性能方面存在错误,它从不自动进行边界检查,因为这需要执行一些额外的指令,而在某些平台上,特定的指令可能非常昂贵。另一方面,C++确实通过标准模板库提供了边界检查(如果需要的话)。
有没有可能我正在访问的位置已经分配给其他程序为其赋值可能会导致该程序在意想不到的时尚?
不,现代操作系统的设计只是为了避免这种情况(出于安全原因)
你必须分配内存,因为尽管每个进程都有自己的4GB空间(由Windows提供),但它们都共享用户机器上的相同xxGB空间。分配内存有助于操作系统知道哪些应用程序需要更多内存,并只将其提供给需要内存的人。
为什么我的"你好世界"需要crysys 2所需的相同RAM?:P
编辑:
好吧,有人误解了我的意思。我没有说没关系,每个人都可以做,什么都不会发生。我只是说这样做不会损害任何外部过程。这仍然是一种未定义的行为,因为没有人知道p+111118上有什么,但ub并不意味着"它可以通过skype点披萨"或其他"令人兴奋的事情",最多只是一种访问违规,仅此而已。
- 将数组的地址分配给变量并删除
- vector.resize()中的分配错误
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- Win32编译器选项和内存分配
- 函数中堆分配的效果与缺少堆分配的情况
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 多个文件的内存分配错误"在抛出 'std :: bad_alloc' what (): std :: bad_alloc 的实例后终止调用" [C++]
- 获取字符串的长度并将其分配给数组
- 将地址分配给本地指针后,公共对象的变量将消失
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 有没有一种方法可以使用placement new将堆叠对象分配给分配的内存
- 我在二维向量中是否正确分配了内存
- 正在尝试重载二进制搜索树分配运算符
- GlobalAlloc而不是其他分配方法
- 自定义先决条件对移动分配运算符有效吗
- 我可以重新分配/覆盖std::字符串吗
- 在c++中使用动态分配的问题
- 当一个新对象被分配到它的地址时,对象是否必须被销毁
- 为什么我可以使用比分配的内存更多的内存
- 使用RAII在给定次数的迭代后重新分配资源