-1

The behaviour of malloc(0) in the C11 standard quotes the following*:

void* malloc( size_t size );


If size is zero, the behavior is implementation defined (null pointer may be returned, or some non-null pointer may be returned that may not be used to access storage, but has to be passed to free).


*(Quoted from cppreference.com rather than the official ISO/IEC 9899:2011 standard, as the former needs to be purchased.)

With this definition I would not have expected the following to work (compiled with icc 18.0.0):

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    double *u = malloc(0);
    u[0] = 3.0;
    printf("The value of u[0] is %lf", u[0]);
    free(u);
    return 0;
}

Surprisingly it worked and gave the output:

The value of u[0] is 3.000000
Process finished with exit code 0

It appears u[0] = 3.0; accesses storage. Had malloc returned NULL instead, then this would cause a segmentation fault.

Have I misunderstood the notion of accessing storage, or is there something else happening?

Update / Clarification

It seems the crux of my question was in the wording "may not be used to access storage", and whether "may not" meant should not or cannot. I had read this as meaning that whatever pointer was returned could not be dereferenced (unless realloc'd).

I have since found the answers to I can use more memory than how much I've allocated with malloc(), why? very applicable to this question. The main point being that writing outside of allocated memory is undefined behaviour, and while this is undoubtedly wrong, it is not guaranteed to throw and error.

The question what's the point in malloc(0)? has several answers mentioning the pointer returned.

oliversm
  • 1,771
  • 4
  • 22
  • 44
  • 5
    Undefined behaviour is ... well ... undefined. – alk Jan 01 '18 at 18:34
  • I'd be curious to know what Valgrind told you when you ran this program using it. – alk Jan 01 '18 at 18:36
  • 1
    C compilers mostly follow a quite anarchistic approach, they simply let you do, what *you* feel is correct, at least if possible, even if its not allowed by whatever rules, and sometimes, surprise, surprise it still *seems* to work ... ;-) – alk Jan 01 '18 at 18:38
  • 1
    The C11 standard is practically the same as [n1570](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) which is *freely* downloadable – Basile Starynkevitch Jan 01 '18 at 19:08
  • You don't specify which, but you should also note that such behaviors can be wildly divergent based on whether you build for Release or Debug. Many times, what will work a hundred times on a Debug build will Halt and Catch Fire with a Release build. – Mark Benningfield Jan 01 '18 at 19:19

4 Answers4

6

You called malloc and asked for a pointer to some memory where you could store some data. You promised you would store no more than 0 bytes of data there. Then you cheated, breaking both your promise and the rules, and stored sizeof(double) number of bytes there instead.

Perhaps you expected that, since you significantly broke the rules, something bad and obvious would go wrong. But it turns out that writing more to a malloc'ed pointer than you're allowed to is an example of undefined behavior, which means that anything — absolutely anything — can happen. And, crucially, one of the things that can happen is that your program can seem to work, even though it's not "supposed" to.

It's a little like this: suppose you're driving along a deserted street in the middle of the night, and you come to a red light. Since there's no cross traffic, and no one else around, and certainly no policemen around, you decide to cheat and drive through the intersection without waiting for the light to turn green. But what's this? No cars crash into you. You don't mow down any pedestrians. No policeman gives you a ticket. How can this be? Running a red light is wrong, right?

Well, of course, there's no surprise. You had already observed that there was no cross traffic, no pedestrians, and no policemen. But if any of those things had suddenly appeared at the last moment and there had been an accident or an uncomfortable police interview, you would have been very much in the wrong.

And the situation is the same with your code. You got "lucky", and nothing obviously went wrong — but the code was wrong.

Undefined behavior is tricky, and hard to think about. But one huge thing to understand about it is that it is not something you can generally expect to get error messages for. In fact, one of the defining aspects of undefined behavior is that the compiler is not required to give you any error messages about it.

If you're curious, see more about undefined behavior at this answer.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
5

Well you are dealing with undefined behavior. That should be clear to you.

No matter what it won't allocate any memory where you can put anything (like you did here).

And beware NULL check won't work here - because it may return NULL when you pass 0 to malloc.

Even it is suggested that whenever you pass some value to malloc then check if it is nonzero positive integer or not. In case it's 0 don't call malloc - it does nothing useful.

user2736738
  • 30,591
  • 5
  • 42
  • 56
3

The statement

double *u = malloc(0);

makes the behaviour of program dependent on the implementation while the statement

u[0] = 3.0;

certainly invokes undefined behavior as standard clearly says that the return pointer shall not be used to access the object. So, you can't dereference the pointer return by malloc(0).

§7.22.3 (p1):

[...] except that the returned pointer shall not be used to access an object.

haccks
  • 104,019
  • 25
  • 176
  • 264
1

As other answers are explaining to you, the behavior is undefined. And you should be scared.

BTW, you could add some printf("u is %p\n", (void*)u); after your malloc.

However, on my Linux/Debian system, malloc is mostly implemented by the C standard library which usually is the GNU glibc (but could be something else, like musl-libc) and which is free software. You could look into glibc's malloc/malloc.c file and understand that malloc(0) gives some valid pointer. For musl-libc, see its src/malloc/malloc.c file.

FWIW, the compiler knows about malloc, mostly thru the malloc function attribute for GCC, and optimizes accordingly.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547