我试图了解C ++中的内存分配。 我想到的一个问题是为什么分配内存是如此必要?如果我们使用内存而不分配它会发生什么? 另外,我很震惊地看到C ++在内存分配方面是多么粗心。如果通过没有边界检查的数组提供对内存的自由访问。
int main()
{
int *p = new int[5];
p[1] = 3;
p[11118] = 9;
cout<<p[11118]<<'\n';
}
以上代码有效,输出9。
在什么情况下将值分配给未分配的内存位置会很危险?什么是潜在的不良影响?是否有可能我正在访问的内存位置已分配给其他程序并为其分配值可能会导致该程序以非常意外的方式崩溃/表现?
答案 0 :(得分:15)
以上代码为Undefined Behaviour。它可以工作,工作不正常,根本无法工作,崩溃或通过Microsoft Skype订购披萨。你不应该依赖未定义的行为:)
答案 1 :(得分:11)
为什么需要分配内存? 因此,您将内存标记为您的内存。没有其他人可以使用它。它还验证实际上有可用的内存。如果你的系统只有1000字节的内存,那么选择字节1500来存储一些数据是个坏主意。
如果我们在不分配内存的情况下使用内存会怎么样? 没人知道。您写的地址可能不存在。可能已经开始使用其他进程,因此您将覆盖其数据。记忆可以得到保护;例如,在前一种情况下,操作系统可能会注意到您正在访问另一个进程声称的内存,并阻止您。您可能拥有该内存区域,但程序的另一部分由于某种原因正在使用它,并且您已经覆盖了自己的数据。
通过没有边界检查的数组自由访问内存。 那段代码不起作用......它现在正如预期的那样运作,但这不是一回事。形式上,这是未定义的行为,因此编译器可以发出代码来执行它想要的任何操作。
在什么情况下将值分配给未分配的内存位置会很危险? 我在上面给出了一些例子。也可以打破你的堆栈。调用函数时,将存储函数应返回的地址。如果你通过粗心的内存访问覆盖了这个值,那么当你离开那个函数时,谁知道你最终会在哪里?也许利用你的程序的人......一个常见的漏洞就是将可执行代码加载到内存的某个部分,然后使用现有程序中的错误来运行它。有一次,在我正在处理的嵌入式设备上,我遇到了fencepost错误导致我的函数返回到其他地方的另一个指令的中间。这应该会让我的芯片崩溃,但幸运的是,该指令的后半部分本身就是一个有效的指令。最终运行的代码序列导致设备获得了感知,并最终完成了我们自己正在处理的项目。现在,它只是在我的地下室玩魔兽。因此是未定义行为的恐怖。
答案 2 :(得分:6)
在堆上分配内存允许动态分配具有动态生存期的动态内存量。
如果你想要边界检查,你可以通过std :: vector :: at()获得它。
在什么情况下将值分配给未分配的内存位置会很危险?
所有情况。
潜在的不良影响是什么?
意外行为。
我访问的内存位置是否可能已分配给其他某个程序并为其分配值可能会导致该程序以非常意外的方式崩溃/表现?
取决于操作系统。
答案 3 :(得分:4)
这似乎有两个问题:
我的回答:
答案 4 :(得分:4)
许多好的答案,但我觉得“为什么我们需要分配内存”缺少一些东西。我认为知道计算机程序的控制流如何在最低级别工作是很重要的,因为C和C ++是硬件上相对较薄的抽象层。
虽然可以在一个巨大的全局范围内编写一个程序,只使用ifs和gotos,但是大多数真实世界的程序被分成 functions ,它们是可以相互调用的独立可移动模块随意。为了跟踪所有数据(参数,返回值,局部变量),所有这些数据都放在一个连续的内存区域,称为堆栈。调用一个函数将放在堆栈上,从函数返回会弹回数据,下一个函数调用会覆盖相同的内存区域。
这样,所有功能代码都可以通过记住偏移相对于其入口点的本地数据来抽象地存储,并且可以从许多不同的上下文调用相同的函数 - 函数的局部变量可能处于不同的绝对地址,但它们总是处于相对于函数入口地址的相对位置。
当函数被调用并返回时,堆栈内存不断被覆盖这一事实意味着您无法在堆栈中放置任何持久数据,即在局部变量中,因为在函数之后局部变量的内存不会保持不变回报。如果您的函数需要在某处存储持久数据,那么必须将该数据存储在其他位置。另一个位置是所谓的堆,您可以通过malloc
或new
手动(也称为“动态”)请求持久存储。内存区域位于其他地方,不会被任何人回收或覆盖,您可以安全地将指针传递给该内存,只要您愿意。唯一的缺点是,除非您手动告诉系统您已完成,否则它将无法将内存用于其他任何内容,这就是您必须手动清理此动态分配的内存的原因。但是需要存储持久信息的函数是我们需要分配内存的原因。
(只是为了完成图片:堆栈上的局部变量被称为“自动分配”。还有“静态分配”,它发生在编译时,是全局变量存在的地方。如果你有全局变量{ {1}},您可以在程序的任何地方高兴地阅读和写信。)
答案 5 :(得分:1)
在大多数操作系统中,主机中可用的物理内存与应用程序代码可以看到的逻辑内存占用量之间存在明显的区别。在大多数情况下,这是由称为内存管理单元(或MMU)的CPU的一部分调解的,它提供了许多有用的目标。
最明显的是,它允许您为应用程序(或多个应用程序)分配比计算机上实际存在的内存更多的内存。当应用程序从内存中请求一些数据时,MMU会调用操作系统来确定内存的真正位置,无论是在内核还是在磁盘上,如果它已被分页。
另一个用途是将一些地址分段用于除应用程序之外的目的,例如,大多数计算机中的GPU都是通过CPU作为核心内存可见的内存区域来控制的,并且它可以读取或写入那个记忆区非常有效。 MMU为操作系统提供了一种使用该内存的方法,但使普通应用程序无法访问它。
由于这种分段,并且由于其他原因,在向操作系统询问某些特定用途的内存之前,应用程序通常无法使用全部地址。例如,在linux上,应用程序通过调用brk
或sbrk
来请求更多核心内存,并且通过调用mmap
来请求内存映射IO。在通过其中一个调用返回地址之前,地址是 unmapped ,访问它将导致段错误,通常会终止违规程序。
某些平台只向已知映射的应用程序公开内存,但C ++在性能方面存在错误,它从不自动绑定检查,因为这需要执行一些额外的指令,并且在某些平台上特别说明可能非常昂贵。另一方面,如果需要,C ++会通过标准模板库提供边界检查。
答案 6 :(得分:-1)
是否有可能是内存 我正在访问的位置 分配给其他一些程序和 为其分配值可能会导致 该程序崩溃/表现得很好 意想不到的时尚?
不,现代操作系统的设计只是为了避免这种情况(出于安全考虑) 而且你必须分配内存,因为虽然每个进程都有自己的4GB空间(由Windows提供),但它们都共享用户在其计算机上拥有的相同xxGB。分配内存有助于操作系统了解哪些应用程序需要更多内存,并仅将其提供给需要它的人 为什么我的“你好世界”需要相同的RAM crysys 2需求? :P
修改强>
好吧,有人误解了我的意思。我没有说它没关系,每个人都可以做到,没有任何事情会发生。我只是说这样做不会伤害任何外部过程。它仍然是未定义的行为,因为没有人知道p + 11118是什么,但ub并不意味着“它可以通过Skype订购披萨”,也不意味着其他“令人兴奋的事情”,最多只是访问冲突,仅此而已。