我可以使用比使用malloc()分配的内存更多的内存,为什么?

woo*_*iap 34 c

char *cp = (char *) malloc(1);
strcpy(cp, "123456789");
puts(cp);
Run Code Online (Sandbox Code Playgroud)

gcc(Linux)和Visual C++ Express上的输出是"123456789",这是否意味着当有空闲内存时,我实际上可以使用比我分配的内容更多的内容malloc()

为什么malloc(0)不导致运行时错误?

谢谢.

PP.*_*PP. 63

您已经提出了一个非常好的问题,也许这会激起您对操作系统的兴趣.您已经知道自己已经设法通过此代码实现了一些您通常不希望做的事情.所以你永远不会在你想要便携的代码中这样做.

更具体地说,这完全取决于您的操作系统和CPU架构,操作系统会为您的程序分配"页面"内存 - 通常这可能是4千字节的顺序.操作系统是页面的守护者,将立即终止尝试访问尚未分配的页面的任何程序.

malloc另一方面,它不是操作系统功能,而是C库调用.它可以通过多种方式实现.您的调用很可能malloc导致来自操作系统的页面请求.然后我malloc会决定给你一个指向该页面内单个字节的指针.当您从给出的位置写入内存时,您只是在操作系统授予您的程序的"页面"中写入,因此操作系统不会看到任何错误的操作.

当然,当您继续调用malloc分配更多内存时,真正的问题就会开始.它最终将返回指向您刚刚写入的位置的指针.当您写入合法的内存位置(从操作系统的角度来看)时,这称为"缓冲区溢出",但可能会覆盖程序的另一部分也将使用的内存.

如果你继续学习这个主题,你将开始理解如何使用这种"缓冲区溢出"技术来利用程序 - 甚至可以开始将汇编语言指令直接写入将由另一个执行的内存区域.你的计划的一部分.

当你到达这个阶段,你将获得很多智慧.但请遵守道德规范,不要用它来破坏宇宙!

PS当我说上面的"操作系统"时,我的意思是"操作系统与特权CPU访问一起".如果进程尝试使用尚未分配给该进程的页面,则CPU和MMU(内存管理单元)会触发特定的操作系统中断或回调.然后,操作系统会干净地关闭您的应用程序,并允许系统继续运行.在过去,在内存管理单元和特权CPU指令之前,您几乎可以随时在内存中的任何位置写入 - 然后您的系统将完全受到内存写入后果的支配!

  • 很好的答案!我已经做了几年的操作系统开发,并在去年修复了MMU问题.到目前为止,我从未想过将某些MMU行为视为对操作系统的回调 - 就像触发的异常和需要完成的工作一样.它简化了事情; 谢谢! (2认同)

GMa*_*ckG 21

不,你得到了不明确的行为.这意味着任何事情都可能发生,从它崩溃(yay)到它"工作"(boo),重新格式化你的硬盘并填充文本文件,说"UB,UB,UB ......"(wat).

想知道之后发生了什么是没有意义的,因为它取决于你的编译器,平台,环境,时间,最喜欢的汽水等等,所有这些都可以随心所欲地做任何他们想要的事情.

更具体地说,使用未分配的任何内存是未定义的行为.你得到一个字节malloc(1),仅此而已.

  • @Henri:它比你的建议更复杂。编译器可以利用某些代码可能产生未定义行为的知识,以不可预测的方式进行优化,无论您对底层操作系统/硬件的了解有多好,而无需对编译器的内部进行特定情况的分析;此外,同一编译器(或不同编译器)的更高版本可能表现不同。例如,编译器可以假设必须调用 UB 的代码永远不会被执行,并且是一致的(以及作为副产品生成高效的代码)。 (2认同)

Job*_*Job 16

当您要求malloc1个字节时,它可能会从操作系统获得1页(通常为4KB).此页面将分配给调用进程,因此只要您不离开页面边界,就不会有任何问题.

但请注意,它绝对是未定义的行为!

考虑以下(假设的)使用时可能发生的情况的示例malloc:

  1. malloc(1)
  2. 如果malloc内部的内存不足,它会问操作系统的更多一些.它通常会收到一个页面.假设它的大小为4KB,地址从0x1000开始
  3. 您的呼叫返回,为您提供要使用的地址0x1000.由于您要求1个字节,因此如果您仅使用地址0x1000 ,则会定义行为.
  4. 由于操作系统刚刚从地址0x1000开始为您的进程分配4KB内存,因此如果您从/向地址0x1000-0x1fff读取/写入内容,它将不会抱怨.所以你可以愉快地这样做,但它是未定义的行为.
  5. 假设你做另一个 malloc(1)
  6. 现在malloc仍然有一些内存,因此不需要向操作系统询问更多内存.它可能会返回地址0x1001.
  7. 如果使用第一个字节给出的地址写入超过1个字节malloc,则在使用第二个地址时会遇到麻烦,malloc因为您将覆盖数据.

所以问题是你一定通过malloc获得1个字节,但可能malloc内部已经给你处理更多的内存分配.

  • @ linuxuser27:对malloc的调用最终向下转到HeapAlloc,对HeapAlloc的调用最终导致对VirtualAlloc的调用,后者分配完整的页面.因此,分配1个字节*将*分配整页.整个页面对于该过程是可读/可写的.如果提交了内存,则进程将使用完整的4 KB. (4认同)
  • @ linuxuser27:"预先分配了一块内存"我认为这是我和Job所说的(总内存使用量),而你在谈论返回的结果. (2认同)