What is the use of null sized memblocks?

Due to concern about the last days / weeks, when I realized that most of my code violates the c99 rules, which leads to undefined behavior, I started to explicitly read the project document ISO / IEC 9899: TC3. In particular, the application “J.2 undefined behavior” Most of them were logical for me, why it is difficult to compose code that violates these rules, or in some cases I might at least think “well, I don’t understand what the problem is with this but I "do so" but there is one point ...

"To access the object (7.20.3), use a non-zero pointer returned by calling the calloc, malloc or realloc function with the requested size zero."

(for anyone who has not read ISO / IEC 9899: TC3 “J.2 undefined behavior”, this section explains what cases will occur in undefined behavior).

So there are so many questions in my head.

First of all:

Why should I allocate a memory block of zero size?

And when I have such a block, what can I do with it?

In order to avoid undefined behavior, maybe I do not want to access memory pointing to it ...

So, I did a bit more research .... looking for several different malloc () pages. and recognized in Linux malloc (3) man:

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

Well, the only thing that helped me was: I now have additional questions for you and for me. The case when a function call with identical parameters under the same conditions can return different results is not so difficult to imagine, OK ... but these different results in most cases should not be different. What allows me to suggest a non-zero pointer to the requested zero-sized block may just be an undesirable side effect. does it mean

if ((void *ptr = malloc (0)) == NULL) { /*...*/ } 

This is not enough? Do I need to handle * alloc calls like this?

 if (X <= 0) { if ((*ptr = malloc (X)) != NULL) { exit (*); } else { /*...*/ } } else { if ((*ptr = malloc (X)) == NULL) { /*...*/ } } 

But even if you expect him to get such

"a unique pointer value, which can subsequently be successfully passed to free ()"

how to work with it? I can change it around OK ... I’m even allowed to free it (by the way, that means I have to free it, as I should do with any other allocated memory, or it's just> you are allowed to not disturb your code stream

What is the difference to just make any pointer that way?

 void *X = (void *)"1234abc"; 

I hope that someone can help me in this philosophy of science, or even better than I am interested in it.

+4
source share
3 answers

Many memory allocation programs have a minimum allocation block that they can support, and will expand any allocation request to this minimum size. Although they could have malloc check if the size is zero and return null, if so, it would be simpler and simpler so that all malloc requests are smaller than the minimum size (including for exactly zero bytes), the size should be kept to a minimum. Since the only code that is likely to request a null-byte malloc will request a buffer whose size was determined at runtime, and since any such code will wait for any buffer it requests to be freed, regardless of its size, this behavior does not caused no difficulties.

Another thing to consider is that if realloc is called several times in a block, it may be advisable to continue using the same memory block if it has enough free space. If a block that has, for example, 256 bytes allocated to it, gets the size to zero, it may be advisable to hold on to the use of previously occupied 256 bytes for any purpose other than re-expanding this memory block. If a call to realloc on a memory block with the requested zero size returns a pointer to the same block, then the system will be able to recognize a subsequent request to re-expand this block and use the previously taken space to satisfy it. If a null byte realloc returns a null pointer, a request to expand this a block of nonzero size will be indistinguishable from a request for allocation of a new one.

Code that uses realloc (or something related to malloc , for that matter) usually does not have any control over the allocation and reallocation of memory, but some implementations of the "malloc library" may benefit from code that doesn at the moment, we need something like that, but most likely it will be needed in the future to redistribute it to zero, and not to release it. Please note that although the usage pattern that is best suited for one library may not be optimal in another, code that uses the malloc library functions in accordance with standards should work, even if not optimally, on any machine that implements their standards are compatible fashion. If you know that over the next year or two the program will most likely be running on a machine with a specific distributor, it may be useful to write the code in a way that is optimal for that distributor. In the future, a program may run on a machine for which its distribution pattern is no longer optimal, but if this machine runs faster and has more RAM (as it will probably be so), suboptimal performance will probably not change.

0
source

C does not support objects of zero size, but the malloc() argument is of type size_t , and there is no good way to prevent the malloc(0) program from being called. This may not be the intention of the programmer, but it is not necessarily literally malloc(0) ; it would rather be malloc(count) , where count is the result of some calculations.

As for the standard allowing two different behaviors, this is simply because the existing implementations (at the time of writing the original standard) did different things, and the authors wanted to avoid breaking existing code. Such code may have already been broken, or at least not portable, but by allowing any behavior, a program that made assumptions about how malloc(0) behaves can continue to work in the system for which it was written .

If you are looking for a consistent explanation, you will not find it. If C was being developed from scratch today, the behavior of malloc(0) would almost certainly have been nailed, anyway. Either this or the behavior would be made undefined, but making it an implementation means that the code should not be checked carefully enough so that it does not pass zero before malloc() .

And in fact, the committee’s decision is documented in C99 Justification , section 7.20.3, pp. 160-161.

It means that:

 void *ptr = malloc(0); free(ptr); 

will work correctly; free() does nothing if its argument is a null pointer.

What can you do with the result of malloc(0) ? Well, if malloc(1024) successful, you can save 1024 bytes in the allocated space. You cannot store bytes in the space allocated by malloc(0) - this is exactly what you requested.

+3
source

Your code may be more explicit, for example:

 if (X <= 0) { ptr = NULL; } else { ptr = malloc(X); if (ptr == NULL) { /*...*/ } } 

and it can be distilled to:

  ptr = (X <= 0) ? NULL : malloc(X); if (ptr == NULL) { /* ... */} 

Today, in C, if malloc cannot get the requested repository, it returns NULL. In addition, this code completely avoids the issue and, more importantly, avoids the potential trap.

0
source

Source: https://habr.com/ru/post/1496009/


All Articles