C program: not enough memory, but still working ... why?

Possible duplicate:
malloc behavior (0)

I am trying to understand the memory allocation in C. Therefore, I am experimenting with malloc . I allocated 0 bytes for this pointer, but still it can still contain an integer. In fact, no matter what number I enter in the malloc parameter, it can still contain any number that I give it. Why is this?

 #include <stdio.h> #include <stdlib.h> int main(void) { int *ptr = (int*)malloc(0); *ptr = 9; printf("%i", *ptr); // 9 free(ptr); return 0; } 

He still prints 9, what about this?

0
c
Jan 31 '13 at 21:42
source share
5 answers

If the size is 0, malloc () returns either NULL or a unique pointer to a value that can subsequently be successfully passed to free ().

I think you are in the second case. In any case, this pointer simply mistakenly finds itself in a region where you can write without generating a segmentation error, but you are probably writing in the space of some other variable that has corrupted its value.

+3
Jan 31 '13 at 21:47
source share

First, your compiler can see these two lines back and optimize them:

 *ptr = 9; printf("%i", *ptr); 

With such a simplified program, your compiler can actually optimize the entire allocated / free memory cycle and use a constant instead. A compiler-optimized version of your program may turn out to be more like just:

 printf("9"); 

The only way to find out if this is really what is happening is to check the assembly that your compiler emits. If you are trying to find out how C works, I recommend that you explicitly disable all compiler optimizations when creating code.

As for your specific use of malloc , remember that you will return a NULL pointer if the distribution fails. Always check the return value of malloc before using it for anything. Blindly dereferencing is a good way to minimize your program.

The link Nick posted gives a good explanation of why malloc(0) may seem to work (note that the significant difference between “works” and “seems to work”). To summarize the information there, malloc(0) allowed to return either NULL or a pointer. If it returns a pointer, you are explicitly forbidden to use it for anything other than passing it to free() . If you try to use such a pointer, you invoke undefined behavior and cannot tell what will happen as a result. It may work for you, but you can rewrite the memory belonging to another program and distort their memory space. In short: nothing good can happen, so leave this pointer alone and don't waste time on malloc(0) .

+2
Jan 31 '13 at 22:08
source share

There are a lot of good answers here. But this is definitely undefined behavior. Some people claim that undefined behavior means that purple dragons can fly from your computer or something like that ... maybe there’s some story behind this outrageous statement that I’m missing, but I promise you that purple dragons will not appear regardless of undefined behavior.

First of all, let me mention that if there is no MMU in a system without virtual memory, your program will have direct access to all the memory in the system, regardless of its address. In such a system, malloc() is just a guy who helps you cut out bits of memory in an orderly manner; the system cannot force you to use only the addresses that malloc() gave you. In a system with virtual memory, the situation is slightly different ... well, very different. But in your program, any code in your program can access any part of the virtual address space displayed through the MMU in real physical memory. It doesn’t matter if you had an address from malloc () or you called rand () and accidentally got an address that falls into the display area of ​​your program; if it is displayed and not marked only by performance, you can read it. And if it is not read-only, you can also write it. Yes. Even if you did not receive it from malloc() .

Consider the possibilities for malloc(0) undefined behavior:

  • malloc(0) returns NULL.

OK, that’s quite simple. Actually, the physical address is 0x00000000 on most computers and even the virtual address is 0x00000000 in all processes, but the OS does not intentionally map memory to this address so that it can catch pointer null references. There's a whole page (usually 4 KB) that just never appears on the map, and maybe even a lot more than 4 KB. Therefore, if you try to read or write a null pointer, even with an offset from it, you will hit these pages of virtual memory that are not even displayed, and the MMU will throw an exception (hardware exception or interrupt) that the OS catches and declares SIGSEGV (on Linux / Unix) or illegal access (on Windows).

  • malloc(0) returns a valid address in the previously unallocated memory of the smallest allocated element.

With this, you actually get the real part of the memory, which you can legally call your own, of a certain size, which you do not know. You really don't have to write anything (and probably don't read) because you don’t know how big it is, and in this case you don’t know if this is a specific case that you are experiencing (see the following cases). If so, the memory block you received is almost guaranteed to be at least 4 bytes and probably 8 bytes, or possibly even more; it all depends on the size of the minimum allowable implementation unit.

  • malloc(0) intentionally returns the address of an unsealed non-NULL memory page.

This is probably a good option to implement, as it will allow you or the system to track and connect the malloc () calls with their corresponding free () calls, but in essence it is the same as returning NULL. If you try to access (read / write) using this pointer, you will crash (SEGV or illegal access).

  • malloc(0) returns the address on another memory card that can be used by "someone else."

I find it unlikely that a commercially available system will use this route, since it serves to simply hide errors, rather than display them as soon as possible. But if that is the case, malloc () will return a pointer somewhere to non-owning memory. If so, of course, you can write whatever you want, but you will corrupt another code memory, although it will be memory in your program process, so you can be sure that you will at least not stomp in another program memory. (I heard someone getting ready to say: “But this is UB, so technically he could die on some other program memory. Yes, in some environments, such as an embedded system, this is correct. A commercial OS would allow one process accessing another process memory is as simple as calling malloc (0), but in fact you simply cannot move from one process to another process memory without going through the OS to do it for you.) Anyway back to reality ... This is where "undefined behavior" really works: if in If you write “someone else's memory” (in your own program process), you will change the behavior of your program in directions that are difficult to predict. Knowing the structure of your program and where everything is laid out in memory, it is completely predictable. But from one system to another, things will be laid out in memory (appearing in different places in the memory), so the effect on one system will not necessarily be the same as the effect on another system or on the same system at another time.

  • And finally ... No, it is. Indeed, indeed, only those four possibilities. You can argue that for the subgrid points of the special case for the last two of the above, but the end result will be the same.
+2
Jan 31 '13 at 22:51
source share

The answer to calling malloc(0) / free() not crashing, you can find it here:

zero size malloc

About *ptr = 9 , just like a buffer overflow (for example, malloc'ing 10 bytes and access to the 11th), you write to memory that you don’t have, and this is looking for problems. In this particular implementation of malloc(0) , a pointer is returned instead of NULL.

On the bottom line, this is wrong, even if it works in the simplest case.

+1
Jan 31 '13 at 21:49
source share

Some memory allocators have the concept of "minimum allocated size". That way, even if you pass zero, it will return a pointer to a word size memory, for example. You need to check the system distributor documentation. But if he returns a pointer to some memory, it would be wrong to rely on it, since the pointer must be passed either to pass realloc () or for free ().

0
Jan 31 '13 at 21:49
source share



All Articles