简体   繁体   English

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

[英]I can use more memory than how much I've allocated with malloc(), why?

char *cp = (char *) malloc(1);
strcpy(cp, "123456789");
puts(cp);

output is "123456789" on both gcc (Linux) and Visual C++ Express, does that mean when there is free memory, I can actually use more than what I've allocated with malloc() ? gcc (Linux) 和 Visual C++ Express 上的输出都是“123456789”,这是否意味着当有空闲内存时,我实际上可以使用比malloc()分配的更多的malloc()

and why malloc(0) doesn't cause runtime error?为什么malloc(0)不会导致运行时错误?

Thanks.谢谢。

You've asked a very good question and maybe this will whet your appetite about operating systems.你问了一个很好的问题,也许这会激发你对操作系统的兴趣。 Already you know you've managed to achieve something with this code that you wouldn't ordinarily expect to do.您已经知道您已经设法使用这段代码实现了一些您通常不会想到的事情。 So you would never do this in code you want to make portable.所以你永远不会在你想要移植的代码中这样做。

To be more specific, and this depends entirely on your operating system and CPU architecture, the operating system allocates "pages" of memory to your program - typically this can be in the order of 4 kilobytes.更具体地说,这完全取决于您的操作系统和 CPU 体系结构,操作系统为您的程序分配内存的“页面” - 通常这可能是 4 KB 的数量级。 The operating system is the guardian of pages and will immediately terminate any program that attempts to access a page it has not been assigned.操作系统是页面的守护者,它将立即终止任何尝试访问未分配页面的程序。

malloc , on the other hand, is not an operating system function but a C library call.另一方面, malloc不是操作系统函数,而是 C 库调用。 It can be implemented in many ways.它可以通过多种方式实现。 It is likely that your call to malloc resulted in a page request from the operating system.您对malloc的调用很可能导致操作系统发出页面请求。 Then malloc would have decided to give you a pointer to a single byte inside that page.然后malloc会决定给你一个指向该页面内单个字节的指针。 When you wrote to the memory from the location you were given you were just writing in a "page" that the operating system had granted your program, and thus the operating system will not see any wrong doing.当您从给定的位置写入内存时,您只是在操作系统授予您程序的“页面”中写入,因此操作系统不会看到任何错误。

The real problems, of course, will begin when you continue to call malloc to assign more memory.当然,真正的问题将在您继续调用malloc分配更多内存时开始。 It will eventually return pointers to the locations you just wrote over.它最终会返回指向您刚刚写入的位置的指针。 This is called a "buffer overflow" when you write to memory locations that are legal (from an operating system perspective) but could potentially be overwriting memory another part of the program will also be using.当您写入合法的内存位置(从操作系统的角度来看)但可能会覆盖程序的另一部分也将使用的内存时,这称为“缓冲区溢出”。

If you continue to learn about this subject you'll begin to understand how programs can be exploited using such "buffer overflow" techniques - even to the point where you begin to write assembly language instructions directly into areas of memory that will be executed by another part of your program.如果您继续学习这个主题,您将开始理解如何使用这种“缓冲区溢出”技术来利用程序——甚至到您开始将汇编语言指令直接写入内存区域的地步,这些指令将由另一个区域执行你程序的一部分。

When you get to this stage you'll have gained much wisdom.到了这个阶段,你的智慧就会增加很多。 But please be ethical and do not use it to wreak havoc in the universe!但是请保持道德,不要用它来对宇宙造成严重破坏!

PS when I say "operating system" above I really mean "operating system in conjunction with privileged CPU access". PS,当我在上面说“操作系统”时,我的意思是“操作系统与特权 CPU 访问相结合”。 The CPU and MMU (memory management unit) triggers particular interrupts or callbacks into the operating system if a process attempts to use a page that has not been allocated to that process.如果进程尝试使用尚未分配给该进程的页面,则 CPU 和 MMU(内存管理单元)会触发操作系统的特定中断或回调。 The operating system then cleanly shuts down your application and allows the system to continue functioning.然后操作系统会彻底关闭您的应用程序并允许系统继续运行。 In the old days, before memory management units and privileged CPU instructions, you could practically write anywhere in memory at any time - and then your system would be totally at the mercy of the consequences of that memory write!在过去,在内存管理单元和特权 CPU 指令之前,您几乎可以随时在内存中的任何位置写入 - 然后您的系统将完全受该内存写入后果的支配!

No. You get undefined behavior .不,你会得到未定义的行为 That means anything can happen, from it crashing (yay) to it "working" (boo), to it reformatting your hard drive and filling it with text files that say "UB, UB, UB..." (wat).这意味着任何事情都可能发生,从它崩溃(yay)到它“工作”(嘘),再到它重新格式化你的硬盘并用文本文件填充它,上面写着“UB,UB,UB...”(wat)。

There's no point in wondering what happens after that, because it depends on your compiler, platform, environment, time of day, favorite soda, etc., all of which can do whatever they want as (in)consistently as they want.想知道之后会发生什么是没有意义的,因为这取决于您的编译器、平台、环境、一天中的时间、最喜欢的苏打水等,所有这些都可以随心所欲地(in)一致地做任何他们想做的事情。

More specifically, using any memory you have not allocated is undefined behavior.更具体地说,使用您尚未分配的任何内存是未定义的行为。 You get one byte from malloc(1) , that's it.你从malloc(1)得到一个字节,就是这样。

When you ask malloc for 1 byte, it will probably get 1 page (typically 4KB) from the operating system.当您向malloc请求 1 个字节时,它可能会从操作系统获得 1 个页面(通常为 4KB)。 This page will be allocated to the calling process so as long as you don't go out of the page boundary, you won't have any problems.这个页面会被分配给调用进程,所以只要你不超出页面边界,就不会有任何问题。

Note, however, that it is definitely undefined behavior!但是请注意,这绝对是未定义的行为!

Consider the following (hypothetical) example of what might happen when using malloc :考虑使用malloc可能发生的情况的以下(假设)示例:

  1. malloc(1)
  2. If malloc is internally out of memory, it will ask the operating system some more.如果malloc内部内存不足,它会向操作系统询问更多。 It will typically receive a page.它通常会收到一个页面。 Say it's 4KB in size with addresses starting at 0x1000假设它的大小为 4KB,地址从 0x1000 开始
  3. Your call returns giving you the address 0x1000 to use.您的调用返回给您要使用的地址 0x1000。 Since you asked for 1 byte, it is defined behavior if you only use the address 0x1000.由于您要求 1 个字节,因此如果您仅使用地址 0x1000,则它是定义的行为
  4. Since the operating system has just allocated 4KB of memory to your process starting at address 0x1000, it will not complain if you read/write something from/to addresses 0x1000-0x1fff.由于操作系统刚刚从地址 0x1000 开始为您的进程分配了 4KB 的内存,因此如果您从/向地址 0x1000-0x1fff 读/写某些内容,它不会抱怨。 So you can happily do so but it is undefined behavior .所以你可以很高兴地这样做,但这是未定义的行为
  5. Let's say you do another malloc(1)假设您执行另一个malloc(1)
  6. Now malloc still has some memory left so it doesn't need to ask the operating system for more.现在malloc还剩下一些内存,所以它不需要向操作系统询问更多。 It will probably return the address 0x1001.它可能会返回地址 0x1001。
  7. If you had written to more than 1 byte using the address given from the first malloc , you will get into troubles when you use the address from the second malloc because you will overwrite the data.如果您使用第一个malloc给出的地址写入了 1 个以上的字节,则在使用第二个malloc的地址时会遇到麻烦,因为您将覆盖数据。

So the point is you definitely get 1 byte from malloc but it might be that malloc internally has more memory allocated to you process.所以关键是你肯定会从 malloc 获得 1 个字节,可能malloc内部分配了更多的内存给你处理。

No. It means that your program behaves badly.不。这意味着您的程序表现不佳。 It writes to a memory location that it does not own.它写入不属于它的内存位置。

You get undefined behavior - anything can happen.你得到未定义的行为 - 任何事情都可能发生。 Don't do it and don't speculate about whether it works.不要这样做,也不要猜测它是否有效。 Maybe it corrupts memory and you don't see it immediately.也许它会破坏记忆,而您不会立即看到它。 Only access memory within the allocated block size.仅访问分配的块大小内的内存。

您可能会被允许使用,直到内存到达某个程序内存或其他点,在该点您的应用程序很可能会因访问受保护的内存而崩溃

So many responses and only one that gives the right explanation.这么多回复,只有一个给出了正确的解释。 While the page size, buffer overflow and undefined behaviour stories are true (and important) they do not exactly answer the original question.虽然页面大小、缓冲区溢出和未定义的行为故事是真实的(而且很重要),但它们并不能完全回答最初的问题。 In fact any sane malloc implementation will allocate at least in size of the alignment requirement of an int or a void * .事实上,任何正常的malloc实现将至少分配intvoid *对齐要求的大小。 Why, because if it allocated only 1 byte then the next chunk of memory wouldn't be aligned anymore.为什么,因为如果它只分配了 1 个字节,那么下一个内存块将不再对齐。 There's always some book keeping data around your allocated blocks, these data structures are nearly always aligned to some multiple of 4. While some architectures can access words on unaligned addresses (x86) they do incure some penalties for doing that, so allocator implementer avoid that.在您分配的块周围总是有一些簿记数据,这些数据结构几乎总是与 4 的某个倍数对齐。虽然某些架构可以访问未对齐地址 (x86) 上的字,但它们确实会因此而招致一些惩罚,因此分配器实现者避免这种情况. Even in slab allocators there's no point in having a 1 byte pool as small size allocs are rare in practice.即使在slab分配器中,拥有一个1字节的池也是没有意义的,因为小尺寸的分配在实践中很少见。 So it is very likely that there's 4 or 8 bytes real room in your malloc'd byte (this doesn't mean you may use that 'feature', it's wrong).因此,您的 malloc 字节中很可能有 4 或 8 个字节的实际空间(这并不意味着您可以使用该“功能”,这是错误的)。

EDIT: Besides, most malloc reserve bigger chunks than asked for to avoid to many copy operations when calling realloc .编辑:此外,大多数malloc保留比要求更大的块,以避免在调用realloc时进行许多复制操作。 As a test you can try using realloc in a loop with growing allocation size and compare the returned pointer, you will see that it changes only after a certain threshold.作为测试,您可以尝试在分配大小不断增长的循环中使用realloc并比较返回的指针,您将看到它仅在某个阈值后发生变化。

You just got lucky there.你只是在那里很幸运。 You are writing to locations which you don't own this leads to undefined behavior.您正在写入不属于您的位置,这会导致未定义的行为。

On most platforms you can not just allocate one byte.在大多数平台上,您不能只分配一个字节。 There is often also a bit of housekeeping done by malloc to remember the amount of allocated memory. malloc 还经常进行一些内务处理以记住分配的内存量。 This yields to the fact that you usually "allocate" memory rounded up to the next 4 or 8 bytes.这导致您通常“分配”四舍五入到下一个 4 或 8 个字节的内存。 But this is not a defined behaviour.但这不是一个明确的行为。

If you use a few bytes more you'll very likeley get an access violation.如果您多使用几个字节,您很可能会遇到访问冲突。

malloc allocates the amount of memory you ask in heap and then return a pointer to void (void *) that can be cast to whatever you want. malloc分配您在堆中请求的内存量,然后返回一个指向 void (void *) 的指针,该指针可以转换为您想要的任何内容。

It is responsibility of the programmer to use only the memory that has been allocate.程序员责任只使用已分配的内存。 Writing (and even reading in protected environment) where you are not supposed can cause all sort of random problems at execution time .在您不应该的地方写入(甚至在受保护的环境中读取)可能会在执行时导致各种随机问题。 If you are lucky your program crash immediately with an exception and you can quite easily find the bug and fix it.如果幸运的话,您的程序会立即崩溃并出现异常,您可以很容易地找到错误并修复它。 If you aren't lucky it will crash randomly or produce unexpected behaviors.如果你不走运,它会随机崩溃或产生意想不到的行为。

For the Murphy's Law , "Anything that can go wrong, will go wrong" and as a corollary of that, "It will go wrong at the right time, producing the most large amount of damage" .对于墨菲定律“任何可能出错的事情都会出错” ,作为其推论, “它会在正确的时间出错,造成最大的损害” It is sadly true.可悲的是,这是真的。 The only way to prevent that, is to avoid that in the language that you can actually do something like that.防止这种情况的唯一方法是在语言中避免这种情况,因为您实际上可以做类似的事情。

Modern languages do not allow the programmer to do write in memory where he/she is not supposed (at least doing standard programming).现代语言不允许程序员做写在内存中,他/她是不应该(至少在做标准的编程)。 That is how Java got a lot of its traction.这就是 Java 获得巨大吸引力的原因。 I prefer C++ to C. You can still make damages using pointers but it is less likely.与 C 相比,我更喜欢C++ 。您仍然可以使用指针造成损害,但可能性较小。 That is the reason why Smart Pointers are so popular.这就是智能指针如此受欢迎的原因。

In order to fix these kind of problems, a debug version of the malloc library can be handy.为了解决这类问题,malloc 库的调试版本可能会很方便。 You need to call a check function periodically to sense if the memory was corrupted.您需要定期调用检查函数来检测内存是否已损坏。 When I used to work intensively on C/C++ at work, we used Rational Purify that in practice replace the standard malloc (new in C++) and free (delete in C++) and it is able to return quite accurate report on where the program did something it was not supposed.当我过去在工作中深入研究 C/C++ 时,我们使用了Rational Purify ,它在实践中取代了标准的 malloc(C++ 中的新功能)和 free(C++ 中的删除),并且它能够返回关于程序执行位置的相当准确的报告这是不应该的。 However you will never be sure 100% that you do not have any error in your code.但是,您永远无法 100% 确定代码中没有任何错误。 If you have a condition that happen extremely rarely, when you execute the program you may not incur in that condition.如果您有一个极少发生的情况,那么当您执行程序时,您可能不会遇到这种情况。 It will eventually happen in production on the most busy day on the most sensitive data (according to Murphy's Law ;-)它最终会在最繁忙的一天发生在最敏感的数据上(根据墨菲定律;-)

To answer your second question, the standard specifically mandates that malloc(0) be legal.为了回答您的第二个问题,该标准特别要求malloc(0)是合法的。 Returned value is implementation-dependent, and can be either NULL or a regular memory address.返回值取决于实现,可以是NULL或常规内存地址。 In either case, you can (and should) legally call free on the return value when done.在任何一种情况下,您都可以(并且应该)在完成后合法地对返回值调用free Even when non- NULL , you must not access data at that address.即使非NULL ,您也不得访问该地址的数据。

It could be that you're in Debug mode, where a call to malloc will actually call _malloc_dbg .可能是您处于 Debug 模式,在这种模式下,对 malloc 的调用实际上会调用_malloc_dbg The debug version will allocate more space than you have requested to cope with buffer overflows.调试版本将分配比您请求处理缓冲区溢出更多的空间。 I guess that if you ran this in Release mode you might (hopefully) get a crash instead.我猜如果你在 Release 模式下运行它,你可能(希望)会崩溃。

您应该在 C++ 中使用 new 和 delete 运算符......并且一个安全的指针来控制该操作不会达到分配的数组的限制......

There is no "C runtime".没有“C 运行时”。 C is glorified assembler. C是美化的汇编程序。 It will happily let you walk all over the address space and do whatever you want with it, which is why it's the language of choice for writing OS kernels.它将愉快地让您遍历地址空间并用它做任何您想做的事情,这就是为什么它是编写操作系统内核的首选语言。 Your program is an example of a heap corruption bug, which is a common security vulnerability.您的程序是堆损坏错误的一个示例,这是一个常见的安全漏洞。 If you wrote a long enough string to that address, you'd eventually overrun the end of the heap and get a segmentation fault, but not before you overwrote a lot of other important things first.如果您向该地址写入了足够长的字符串,您最终会超出堆的末尾并出现分段错误,但在您首先覆盖许多其他重要内容之前不会发生这种情况。

When malloc() doesn't have enough free memory in its reserve pool to satisfy an allocation, it grabs pages from the kernel in chunks of at least 4 kb, and often much larger, so you're probably writing into reserved but un-malloc()ed space when you initially exceed the bounds of your allocation, which is why your test case always works.当 malloc() 在其保留池中没有足够的可用内存来满足分配时,它会以至少 4 kb 的块从内核中抓取页面,并且通常更大,因此您可能正在写入保留但未-当您最初超出分配范围时 malloc()ed 空间,这就是您的测试用例始终有效的原因。 Actually honoring allocation addresses and sizes is completely voluntary, so you can assign a random address to a pointer, without calling malloc() at all, and start working with that as a character string, and as long as that random address happens to be in a writable memory segment like the heap or the stack, everything will seem to work, at least until you try to use whatever memory you were corrupting by doing so.实际上遵守分配地址和大小是完全自愿的,因此您可以为指针分配一个随机地址,根本不调用 malloc(),并开始将其作为字符串处理,只要该随机地址恰好在像堆或堆栈这样的可写内存段,一切似乎都可以正常工作,至少在您尝试使用因这样做而损坏的任何内存之前。

strcpy() doesn't check if the memory it's writing to is allocated. strcpy() 不检查它正在写入的内存是否已分配。 It just takes the destination address and writes the source character by character until it reaches the '\\0'.它只是获取目标地址并逐个字符地写入源地址,直到到达 '\\0'。 So, if the destination memory allocated is smaller than the source, you just wrote over memory.因此,如果分配的目标内存小于源内存,则您只需覆盖内存。 This is a dangerous bug because it is very hard to track down.这是一个危险的错误,因为很难追踪。

puts() writes the string until it reaches '\\0'. puts() 写入字符串直到它到达 '\\0'。

My guess is that malloc(0) only returns NULL and not cause a run-time error.我的猜测是 malloc(0) 只返回 NULL 而不会导致运行时错误。

My answer is in responce to Why does printf not seg fault or produce garbage?我的答案是响应为什么 printf 不会段错误或产生垃圾?

From

The C programming language by Denis Ritchie & Kernighan Denis Ritchie & Kernighan 的 C 编程语言

 typedef long Align;    /* for alignment to long boundary */
   union header {         /* block header */
       struct {
           union header *ptr; /* next block if on free list */
           unsigned size;     /* size of this block */
       } s;
       Align x;           /* force alignment of blocks */
   };
   typedef union header Header;

The Align field is never used;从不使用Align字段; it just forces each header to be aligned on a worst-case boundary .它只是强制每个标头在最坏情况边界上对齐 In malloc ,the requested size in characters is rounded up to the proper number of header-sized units;malloc ,请求的字符大小被四舍五入到适当的头部大小单元数; the block that will be allocated contains one more unit, for the header itself, and this is the value recorded in the size field of the header.将被分配的块还包含一个单元,用于header本身,这是头部size字段中记录的值。 The pointer returned by malloc points at the free space, not at the header itself . malloc 返回的指针指向空闲空间,而不是头本身

The user can do anything with the space requested, but if anything is written outside of the allocated space the list is likely to be scrambled.用户可以对请求的空间做任何事情,但如果在分配的空间之外写入任何内容,则列表可能会被打乱。

   -----------------------------------------
   |        |     SIZE     |               |
   -----------------------------------------
     |        |
  points to   |-----address returned touser
   next free
   block
        -> a block returned by malloc 

In statement在声明中

char* test = malloc(1);

malloc() will try to search consecutive bytes from the heap section of RAM if requested bytes are available and it returns the address as below如果请求的字节可用,malloc() 将尝试从 RAM 的堆部分搜索连续字节,并返回如下address

 --------------------------------------------------------------
| free memory  | memory in size allocated for user |           |
----------------------------------------------------------------
                                                              0x100(assume address returned by malloc)
                                                              test

So when malloc(1) executed it won't allocate just 1 byte, it allocated some extra bytes to maintain above structure/heap table.因此,当malloc(1)执行时,它不会只分配1个字节,而是分配了一些extra字节来维护上面的结构/堆表。 you can find out how much actual memory allocated when you requested only 1 byte by printing test[-1] because just to before that block contain the size.您可以通过打印test[-1]来找出当您仅请求1个字节时分配了多少实际内存,因为就在该块之前包含大小。

char* test = malloc(1);
printf("memory allocated in bytes = %d\n",test[-1]);

如果传递的大小为零,并且 ptr 不为 NULL,则调用等效于 free。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么malloc()分配的内存比要求的多,并且如何在Mac OS X中禁用malloc? - Why is malloc() allocating more memory than demanded and how can I disable malloc from doing this in Mac OS X? C.使用的内存多于malloc中分配的内存-我可以返回sth,结束程序而不是使其崩溃吗? - C. Using more memory than allocated in malloc - can I return sth, end program instead of letting it crash? Malloc - >分配了多少内存? - Malloc -> how much memory has been allocated? 为什么我在函数中分配了指针内存,但它也是NULL? - Why I've allocated a pointer memory in a function, but it's also NULL? 如何计算malloc分配的内存块的结束地址? - How do I calculate end address of memory block allocated by malloc? 谁能解释为什么输出多于分配的内存? - Can anyone explain why the output is more than the memory allocated? 跟踪malloc分配的内存量 - keeping track of how much memory malloc has allocated 为什么我的缓冲区在堆栈上分配的内存比我要求的多? - Why my buffer got more memory allocated on stack than what I asked for? gets()如何超过malloc()分配的内存? - How can gets() exceed memory allocated by malloc()? 我如何正确处理C中的malloc失败,尤其是当有多个malloc时? - How can I correctly handle malloc failure in C, especially when there is more than one malloc?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM