malloc(0)

J

john smith

Can someone please explain to me what is happening when I do a malloc(0).

This is what I did.

int* p = (int*)malloc(0);

Then I printed the value of p and of course it was non-null.

But has it allocated memory or what?

I would think that it would return a null.

So, can someone please explain what is going on?

Many thanks.
 
D

Derrick Coetzee

john said:
int* p = (int*)malloc(0);

But has it allocated memory or what? I would think that it would return a null.

It has indeed. It has allocated a block containing no bytes at all.
According to the C++ standard, malloc() is the same as in C, and
the comp.lang.c FAQ says:

11.26: What should malloc(0) do? Return a null pointer or a pointer to
0 bytes?

A: The ANSI/ISO Standard says that it may do either; the behavior
is implementation-defined (see question 11.33).

References: ISO Sec. 7.10.3; PCS Sec. 16.1 p. 386.
 
V

Victor Bazarov

john smith said:
Can someone please explain to me what is happening when I do a malloc(0).

This is what I did.

int* p = (int*)malloc(0);

Then I printed the value of p and of course it was non-null.

But has it allocated memory or what?

I would think that it would return a null.

So, can someone please explain what is going on?

The behaviour of 'malloc' (and 'calloc' and 'realloc') when the size
passed in is zero is implementation-defined (so RTFM) and can be
either of two: the return value is a null pointer or it is non-null
(as if the size is non-zero), but the pointer cannot be used to access
an object.

Victor
 
K

Kai-Uwe Bux

john said:
Can someone please explain to me what is happening when I do a malloc(0).

This is what I did.

int* p = (int*)malloc(0);

Then I printed the value of p and of course it was non-null.

But has it allocated memory or what?

Sure, it has allocated a block of length 0. I have no idea what you want to
do with a block that small, but I am sure that malloc has just allocated a
block of that size, after all that is what malloc is supposed to do
according to its man page.

I would think that it would return a null.

Why? It returns the location in memory where the allocated block starts.
That this block has *length* 0 does not imply that it should start at the
*location* 0.
So, can someone please explain what is going on?

Nothing is going on: allocating a block of length 0 is pretty useless as
you can store nothing in there. Yet, be sure to free it once you have no
need for it anymore.

Many thanks.



Best

Kai-Uwe Bux
 
E

E. Robert Tisdale

Victor said:
The behaviour of 'malloc' (and 'calloc' and 'realloc')
when the size passed in is zero is implementation-defined (so RTFM)
and can be either of two:
the return value is a null pointer or it is non-null
(as if the size is non-zero),
but the pointer cannot be used to access an object.
cat main.cc
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
if (1 < argc) {
const
size_t n = atoi(argv[1]);
int* p = (int*)malloc(n*sizeof(int));
fprintf(stdout, "%d = p[-1]\t", p[-1]);

for (size_t j = 0; j < 4; ++j)
p[j] = j;

for (size_t j = 0; j < 4; ++j)
fprintf(stdout, "%d = p[%d]\t", p[j], j);
fprintf(stdout, "\n");

}
return EXIT_SUCCESS;
}
g++ -Wall -ansi -pedantic -o main main.cc
./main 0
17 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]
. /main 1
17 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]
17 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]
17 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]
25 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]
 
K

Kai-Uwe Bux

E. Robert Tisdale said:
Victor said:
The behaviour of 'malloc' (and 'calloc' and 'realloc')
when the size passed in is zero is implementation-defined (so RTFM)
and can be either of two:
the return value is a null pointer or it is non-null
(as if the size is non-zero),
but the pointer cannot be used to access an object.
cat main.cc
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
if (1 < argc) {
const
size_t n = atoi(argv[1]);
int* p = (int*)malloc(n*sizeof(int));
fprintf(stdout, "%d = p[-1]\t", p[-1]);

for (size_t j = 0; j < 4; ++j)
p[j] = j;

for (size_t j = 0; j < 4; ++j)
fprintf(stdout, "%d = p[%d]\t", p[j], j);
fprintf(stdout, "\n");

}
return EXIT_SUCCESS;
}
g++ -Wall -ansi -pedantic -o main main.cc
./main 0
17 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]
. /main 1
17 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]
17 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]
17 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]
25 = p[-1] 0 = p[0] 1 = p[1] 2 = p[2] 3 = p[3]

And your point is?

I guess, you wrote a program whose behavior is undefined; and you happend
to get away with it this time for a particular implementation.

#include <cstdlib>
#include <iostream>

struct xxx {
char banner [26];
};

int main( void ) {
xxx* p = (xxx*)malloc(0*sizeof(xxx));
xxx* q = (xxx*)malloc(0*sizeof(xxx));

std::strcpy( p->banner, "Hello folks, how are you?" );
std::strcpy( q->banner, "Hello folks, how are you?" );

std::cout << "p->banner: " << p->banner << "\n";
std::cout << "q->banner: " << q->banner << "\n";

return 0;
}
g++ scratch.cc
a.out
p->banner: Hello folks, howHello folks, how are you?
q->banner: Hello folks, how are you?



Best

Kai-Uwe Bux
 
E

E. Robert Tisdale

Kai-Uwe Bux said:
I guess, you wrote a program whose behavior is undefined;
and you happend to get away with it this time
for a particular implementation.

#include <cstdlib>
#include <iostream>

struct xxx {
char banner [26];
};

int main( void ) {
xxx* p = (xxx*)malloc(0*sizeof(xxx));
xxx* q = (xxx*)malloc(0*sizeof(xxx));

std::strcpy( p->banner, "Hello folks, how are you?" );
std::strcpy( q->banner, "Hello folks, how are you?" );

std::cout << "p->banner: " << p->banner << "\n";
std::cout << "q->banner: " << q->banner << "\n";

return 0;
}
g++ scratch.cc
a.out

p->banner: Hello folks, howHello folks, how are you?
q->banner: Hello folks, how are you?

You should always free storage that you allocate:
cat scratch.cc
#include <cstdlib>
#include <iostream>

typedef struct xxx {
char banner [26];
} xxx;

int main(int argc, char* argv[]) {
xxx* p = (xxx*)malloc(0*sizeof(xxx));
xxx* q = (xxx*)malloc(0*sizeof(xxx));

std::strcpy( p->banner, "Hello folks, how are you?" );
std::strcpy( q->banner, "Hello folks, how are you?" );

std::cout << "p->banner: " << p->banner << "\n";
std::cout << "q->banner: " << q->banner << "\n";

free((void*)q);
free((void*)p);

return EXIT_SUCCESS;
}
g++ -Wall -ansi -pedantic -o scratch scratch.cc
./scratch
p->banner: Hello folks, howHello folks, how are you?
q->banner: Hello folks, how are you?
Segmentation fault (core dumped)
 
K

Kai-Uwe Bux

E. Robert Tisdale said:
Kai-Uwe Bux said:
I guess, you wrote a program whose behavior is undefined;
and you happend to get away with it this time
for a particular implementation.

#include <cstdlib>
#include <iostream>

struct xxx {
char banner [26];
};

int main( void ) {
xxx* p = (xxx*)malloc(0*sizeof(xxx));
xxx* q = (xxx*)malloc(0*sizeof(xxx));

std::strcpy( p->banner, "Hello folks, how are you?" );
std::strcpy( q->banner, "Hello folks, how are you?" );

std::cout << "p->banner: " << p->banner << "\n";
std::cout << "q->banner: " << q->banner << "\n";

return 0;
}
g++ scratch.cc
a.out

p->banner: Hello folks, howHello folks, how are you?
q->banner: Hello folks, how are you?

You should always free storage that you allocate:
^^^^^^

Why?

In the case under discussion, the operating system will reclaim all
allocated memory since the program is about to die anyway. I do see no
point in freeing the memory myself.


Best

Kai-Uwe Bux
cat scratch.cc
#include <cstdlib>
#include <iostream>

typedef struct xxx {
char banner [26];
} xxx;

int main(int argc, char* argv[]) {
xxx* p = (xxx*)malloc(0*sizeof(xxx));
xxx* q = (xxx*)malloc(0*sizeof(xxx));

std::strcpy( p->banner, "Hello folks, how are you?" );
std::strcpy( q->banner, "Hello folks, how are you?" );

std::cout << "p->banner: " << p->banner << "\n";
std::cout << "q->banner: " << q->banner << "\n";

free((void*)q);
free((void*)p);

return EXIT_SUCCESS;
}
g++ -Wall -ansi -pedantic -o scratch scratch.cc
./scratch
p->banner: Hello folks, howHello folks, how are you?
q->banner: Hello folks, how are you?
Segmentation fault (core dumped)
 
G

Gianni Mariani

Kai-Uwe Bux said:
E. Robert Tisdale wrote: .....

Why?

In the case under discussion, the operating system will reclaim all
allocated memory since the program is about to die anyway. I do see no
point in freeing the memory myself.

While in this particular scenario, you're right, in practice this is the
exception. Since this is an example, it should demonstrate the more
general scenario and hence why free() should be called.
 
J

Jerry Coffin

john smith said:
Can someone please explain to me what is happening when I do a malloc(0).

This is what I did.

int* p = (int*)malloc(0);

Then I printed the value of p and of course it was non-null.

But has it allocated memory or what?

I would think that it would return a null.

Using malloc with an argument of 0 gives implementation-defined
results -- it can return a null pointer or it can return a non-null
pointer to a block that you can't dereference.

As an aside, using new instead of malloc would give a non-null pointer
that you can't dereference (or more accurately, attempting to
dereference it would give undefined behavior).
 
J

Jack Klein

^^^^^^

Why?

In the case under discussion, the operating system will reclaim all
allocated memory since the program is about to die anyway. I do see no
point in freeing the memory myself.

Much as I dislike agreeing with the troll Tisdale, he is perfectly
correct here.

Exactly where in the C++ standard does it state that allocated memory
not released by a program will be reclaimed by the operating system?
Where is the guarantee that this works on all implementations and all
platforms?

And of course what happens when someone tries to reuse the code inside
a larger program by renaming main()?

Sloppy programming habits should never be encouraged. Or even
tolerated.
 
J

Jack Klein

Sure, it has allocated a block of length 0. I have no idea what you want to
do with a block that small, but I am sure that malloc has just allocated a
block of that size, after all that is what malloc is supposed to do
according to its man page.

That is one of the two defined actions that malloc() may take. The
other is to return a null pointer.
Why? It returns the location in memory where the allocated block starts.
That this block has *length* 0 does not imply that it should start at the
*location* 0.

What do you think that a null pointer has to do with location 0? A
null pointer does not point to location 0. It does not even have an
all-bits zero representation on all platforms.

If there is a location 0 on a platform and it is valid for a C (or
C++) object to reside there, then a pointer to such an object would be
a pointer to location 0 and it would most specifically not be a null
pointer.
 
D

David Hilsee

E. Robert Tisdale said:
Kai-Uwe Bux said:
I guess, you wrote a program whose behavior is undefined;
and you happend to get away with it this time
for a particular implementation.

#include <cstdlib>
#include <iostream>

struct xxx {
char banner [26];
};

int main( void ) {
xxx* p = (xxx*)malloc(0*sizeof(xxx));
xxx* q = (xxx*)malloc(0*sizeof(xxx));

std::strcpy( p->banner, "Hello folks, how are you?" );
std::strcpy( q->banner, "Hello folks, how are you?" );

std::cout << "p->banner: " << p->banner << "\n";
std::cout << "q->banner: " << q->banner << "\n";

return 0;
}
g++ scratch.cc
a.out

p->banner: Hello folks, howHello folks, how are you?
q->banner: Hello folks, how are you?

You should always free storage that you allocate:
<snip>

You didn't free the storage in your original example, either. What happens
when you "correct" it?
 
K

Kai-Uwe Bux

Jack said:
That is one of the two defined actions that malloc() may take. The
other is to return a null pointer.

Actually, I was completely wrong, but maybe notin the way you may have had
in mind primarily.

As far as I can tell from the standard, which I have read now, every call
to malloc, regardless of the argument, may return a null pointer any time.
As far as I can see, the standard makes no guarantees that calls to malloc
"succeed". So there is no difference between malloc(0) and malloc(n). The
standard just allows explicitly that every call malloc(0) may fail,
however, I cannot see that it does not allow that for other values too. In
fact, I do not see any language in the standard that would rule out the
following most useless implementation:

void* malloc( size_t size ) {
return( NULL );
}


Besides, the OP explictly mentioned that malloc(0) did not return 0 and
asked what was going on. So, I was describing what happens for a succesful
call to malloc(0), and I was mistaken on a subtle point. I thought,
incorrectly, that malloc(0) had allocated a chunk of size 0. In particular,
I thought that subsequent calls to malloc(0) may return the same pointer.
That, however, may not be allowed:

If the size of the space requested is zero, the behavior is
implementation-defined: either a null-pointer is returned, or
the behavior is *as if the size were some nonzero value*, except
that the returned pointer shall not be used to access an object.

Thus, if malloc(0) does not return 0, it shall behave as though it was
called with a non-zero argument. I read this to imply that two successive
calls to malloc(0), neither of which returns 0, must return different
values. This is an extra requirement that would not otherwise follow from
the usual requirement that allocated blocks are to be disjoint.

What do you think that a null pointer has to do with location 0? A
null pointer does not point to location 0. It does not even have an
all-bits zero representation on all platforms.

If there is a location 0 on a platform and it is valid for a C (or
C++) object to reside there, then a pointer to such an object would be
a pointer to location 0 and it would most specifically not be a null
pointer.

You are right, I was sloppy.

Just an asside triggered by your remark: is the numerical constant 0 (say
as an int or unsigned int) required to have an all-bits zeror
epresentation?


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Jack said:
Much as I dislike agreeing with the troll Tisdale, he is perfectly
correct here.

Exactly where in the C++ standard does it state that allocated memory
not released by a program will be reclaimed by the operating system?
Where is the guarantee that this works on all implementations and all
platforms?

Interesting question, however, your concern will not be cured by the call
to free() that was proposed. From the C standard:

The free function causes the space pointed to by ptr to be deallocated,
that is, made available for further allocation. ...

Since this part is inherited by C++, and since C++ does only specify the
observable behavior of a single instruction sequence, the phrase "available
for further allocation" just refers to the very C++ programm that is
calling free(), i.e, subsequence calls to malloc() or new() may now return
this memory. There is, as far as I can see, no gurantee whatsoever in the C
or C++ standard that space deallocated by free() or delete() is returned to
the operating system (thereby becomming available to *other programs*)?

Where do you get your guarantee that freeing memory works as you seem to
expect?

And of course what happens when someone tries to reuse the code inside
a larger program by renaming main()?

Sloppy programming habits should never be encouraged. Or even tolerated.

My understanding of sloppyness is something along the lines of "not
thinking about what I code". I question rules that contain the word
"always" or "never" because, more often than not, they encourage sloppyness
in this sense. For example, reusing main() by renaming without reading it
would be sloppy.

Besides, the example was meant to illustrate a point under discussion and
not teach how to programm -- it exhibits undefined behavior anyway.


Best

Kai-Uwe Bux
 
I

Ioannis Vranos

Kai-Uwe Bux said:
Thus, if malloc(0) does not return 0, it shall behave as though it was
called with a non-zero argument. I read this to imply that two successive
calls to malloc(0), neither of which returns 0, must return different
values.


Now we got in this really useless stuff, but the standard does not
require pointers on different objects to have different values.

Pointer comparison may happen only for the same object or sequence of
objects.






Regards,

Ioannis Vranos

http://www23.brinkster.com/noicys
 
K

Kai-Uwe Bux

Ioannis said:
Now we got in this really useless stuff, but the standard does not
require pointers on different objects to have different values.

Pointer comparison may happen only for the same object or sequence of
objects.

For comparisions of pointers to objects not in the same sequence, I think
the standard allows the use of std::less< PointerType > and its relatives.
By 20.3.3/8 this is supposed to yield a total order.

Nonetheless, I am confused. I was thinking that malloc(n) is inherited from
C and governed by the language of the C standard with some modifications
from 20.4.6. Thus, malloc() returns void* (whatever that is) and not a
pointer to some object. The requirement is just that it is alligned
suitably so that it can be assigned to any pointer variable.

Moreover, malloc(n) shall, if called successfully, return the lowest byte
of some chunk of size at least n. Now, how am I to interpret the rule that
subsequent calls to malloc shall return pointers to disjoint chunks? Do you
claim that it was permissible for the returned values of type void* to
agree, yet they would somehow manage to refer to the lowest bytes of
disjoint chunks of memory?

I guess, what I claim is that

#include <iostream>
#include <functional>

int main ( void ) {
void* p = malloc(0);
void* q = malloc(0);
if ( ( p != 0 ) && ( q != 0 ) ){
if ( std::equal_to< void* >()( p, q ) ) {
std::cout << "unexpected result.\n";
}
}
}

is not supposed to print something.


Best

Kai-Uwe Bux
 
O

Old Wolf

It does for objects of the same type. I wonder what 'different values'
means for objects of incompatible types. For example:
union { int a; float b; } u;
can you say that &u.a and &u.b are the same value? Is it well-defined
to compare &u.a to &u.b (even after casting to void *) ? Of course,
casting to void* means the value may change.

Pointers to different objects of compatible types can be checked
with ==.
For comparisions of pointers to objects not in the same sequence, I think
the standard allows the use of std::less< PointerType > and its relatives.
By 20.3.3/8 this is supposed to yield a total order.
Useful.

Moreover, malloc(n) shall, if called successfully, return the lowest byte
of some chunk of size at least n. Now, how am I to interpret the rule that
subsequent calls to malloc shall return pointers to disjoint chunks? Do you
claim that it was permissible for the returned values of type void* to
agree, yet they would somehow manage to refer to the lowest bytes of
disjoint chunks of memory?

No, the values returned by malloc must all be different, ie.
void *p = malloc(0);
void *q = malloc(0);
assert( !p || !q || p != q );
 
G

Gianni Mariani

Old Wolf wrote:
.... really major nit pick ... excuse me.
No, the values returned by malloc must all be different, ie.
void *p = malloc(0);
void *q = malloc(0);
assert( !p || !q || p != q );

assert( !p || p != q );

I can move on now that I have removed that thorn :)
 
K

Kai-Uwe Bux

Old said:
It does for objects of the same type. I wonder what 'different values'
means for objects of incompatible types. For example:
union { int a; float b; } u;
can you say that &u.a and &u.b are the same value? Is it well-defined
to compare &u.a to &u.b (even after casting to void *) ? Of course,
casting to void* means the value may change.

You can say that &u.a and &u.b have the same value when converted to void*.
From the standard [5.9]:

If two pointers point to data members of the same union object, they
compare equal (after conversion to void*, if necessary).

Pointers to different objects of compatible types can be checked
with ==.


Useful.

Essential: you need to have this for containers like std::set<T*>. By 5.9,
the built in operator< is undefined for T* if the pointers do not happen to
point to members of the same object or array.

No, the values returned by malloc must all be different, ie.
void *p = malloc(0);
void *q = malloc(0);
assert( !p || !q || p != q );

That is how I understand the language of the standard, too.


Best

Kai-Uwe Bux
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top