Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

double* p=malloc(1) // OK?

7 views
Skip to first unread message

blargg

unread,
Jun 1, 2009, 12:06:28 AM6/1/09
to
Can the result of malloc always be assigned to a pointer whose
referenced type has stricter alignment requirements than char, for
example a double*, without corrupting the pointer value?

double* d = malloc( 1 ); // pointer value not corrupted?
free( d ); // free() receives same value malloc() returned?

Or more verbosely,

void* v = malloc( 1 );
double* d = v;
void* v2 = d; // convert back
assert( v2 == v ); // no information lost converting to double*?

I'm interested in whether the above is STRICTLY portable, not whether
it works in practice, as it probably will due to the fact that most
malloc implementations have some block overhead that makes aligning
even one-byte allocations no more wasteful than not, and most
architectures use the same representation for double* as void*.

The result of an allocation function is defined to be aligned suitably
for any object type, without any qualification based on the size of
the allocation. Malloc is defined to allocate space for an object
whose size is passed, which might suggest that the above is invalid,
or merely the more obvious fact that the object ACCESSED must not be
larger than the size passed.

Even if the allocation function semantics don't give an answer,
perhaps those of a pointer do. The definition of a pointer states that
a pointer refers to an entity of the referenced type, which implies
that an accessible object must exist, even if it's never accessed. The
compiler could presumably access said object even if user code never
did, treating the original code as

double* d = malloc( 1 );
double temp = *d; // inserted by compiler
free( d );

Even if we made d a pointer to volatile double, preventing the
compiler from accessing the object, there could still be some kind of
other implementation-specific activity that failed if there weren't an
actual double object pointed to by d (as could be the case for
malloc(1)).

Here are three relevant sections from the C99 standard:

7.20.3 Memory management functions
> 1 [...] The pointer returned if the allocation succeeds is suitably
> aligned so that it may be assigned to a pointer to any type of object
> and then used to access such an object or an array of such objects in
> the space allocated (until the space is explicitly deallocated). [...]

7.20.3.3 The malloc function
[...]
> 2 The malloc function allocates space for an object whose size is
> specified by size and whose value is indeterminate.

6.2.5 Types
[...]
> 20 Any number of derived types can be constructed from the object,
> function, and incomplete types, as follows:
[...]
> - A pointer type may be derived from a function type, an object type,
> or an incomplete type, called the referenced type. A pointer type
> describes an object whose value provides a reference to an entity of
> the referenced type.

James Kuyper

unread,
Jun 1, 2009, 6:29:26 AM6/1/09
to
blargg wrote:
> Can the result of malloc always be assigned to a pointer whose
> referenced type has stricter alignment requirements than char, for
> example a double*, without corrupting the pointer value?

Yes, as indicated by 7.20.3p1, which you yourself cited in your message.

> double* d = malloc( 1 ); // pointer value not corrupted?
> free( d ); // free() receives same value malloc() returned?
>
> Or more verbosely,
>
> void* v = malloc( 1 );
> double* d = v;
> void* v2 = d; // convert back
> assert( v2 == v ); // no information lost converting to double*?
>
> I'm interested in whether the above is STRICTLY portable,

Yes.


> The result of an allocation function is defined to be aligned suitably
> for any object type, without any qualification based on the size of
> the allocation. Malloc is defined to allocate space for an object
> whose size is passed, which might suggest that the above is invalid,
> or merely the more obvious fact that the object ACCESSED must not be
> larger than the size passed.

The "more obvious fact" is in fact what the standard is saying.

Philipp Klaus Krause

unread,
Jun 1, 2009, 6:52:41 AM6/1/09
to
blargg schrieb:

> double* d = malloc( 1 );

Shouldn't that be
double* d = malloc(sizeof(double));

Philipp

blargg

unread,
Jun 1, 2009, 7:56:51 AM6/1/09
to

No, it was a somewhat esoteric question (and why it was asked on
comp.std.c rather than comp.lang.c).

James Kuyper

unread,
Jun 1, 2009, 7:57:16 AM6/1/09
to

That's the point of his question - if allocating less than the space
required to store an object of a given type, is malloc() required to
return a pointer that can be converted into a pointer to that type?

The answer is yes, but it's less than perfectly obvious. What's less
obvious is why one would want to write code which depends upon this feature.

blargg

unread,
Jun 1, 2009, 8:05:36 AM6/1/09
to
James Kuyper wrote:
> blargg wrote:
> > Can the result of malloc always be assigned to a pointer whose
> > referenced type has stricter alignment requirements than char, for
> > example a double*, without corrupting the pointer value?
>
> Yes, as indicated by 7.20.3p1, which you yourself cited in your message.

One interesting result of this is that it on a machine whose double*
cannot store unaligned pointers, it forces malloc to waste max_alignment-n
for allocations of n bytes where n<max_alignment. For example, if said
machin's double requires 8-byte alignment, allocating 1000 one-byte
objects with malloc would require it to waste 7000 bytes of space to
ensure each was aligned (http://cantrip.org/wave12.html in the "A Memory
Manager for C++" section partway down, describes an allocator with very
little overhead, which isn't C++-specific either, despite the title).
Fortunately, most machines' double* can store even unaligned pointers,
allowing malloc to return unaligned pointers for really small objects.

Vincent Lefevre

unread,
Jun 1, 2009, 9:28:21 PM6/1/09
to
In article <blargg.ei3-31...@192.168.1.4>,
blargg <blarg...@gishpuppy.com> wrote:

> Or more verbosely,

> void* v = malloc( 1 );
> double* d = v;
> void* v2 = d; // convert back
> assert( v2 == v ); // no information lost converting to double*?

> I'm interested in whether the above is STRICTLY portable,

I wonder... If it is "STRICTLY portable", then how about the following?

double *d2 = d + 0;

It seems that since d does not point to any double (or one past
the last element of an array of doubles), this operation isn't
defined by 6.5.6#8, thus it is undefined behavior.

--
Vincent Lef�vre <vin...@vinc17.org> - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)

blargg

unread,
Jun 2, 2009, 2:52:39 AM6/2/09
to
Vincent Lefevre wrote:

> blargg wrote:
>> Or more verbosely,
>>
>> void* v = malloc( 1 );
>> double* d = v;
>> void* v2 = d; // convert back
>> assert( v2 == v ); // no information lost converting to double*?
>>
>> I'm interested in whether the above is STRICTLY portable,
>
> I wonder... If it is "STRICTLY portable", then how about the following?
>
> double *d2 = d + 0;
>
> It seems that since d does not point to any double (or one past
> the last element of an array of doubles), this operation isn't
> defined by 6.5.6#8, thus it is undefined behavior.

Couldn't the result of malloc(1) be treated as a past-the-end pointer for
an array of zero doubles? If so, then (double*)malloc(1)+0 would be valid.

Wojtek Lerch

unread,
Jun 2, 2009, 9:11:23 AM6/2/09
to
"blargg" <blarg...@gishpuppy.com> wrote in message
news:blargg.ei3-02...@192.168.1.4...

> Couldn't the result of malloc(1) be treated as a past-the-end pointer for
> an array of zero doubles? If so, then (double*)malloc(1)+0 would be valid.

No it couldn't, because there's no such thing as an array of zero doubles in
C. Besides, if the result of malloc() were equivalent to a pointer to past
the end of any array, you couldn't convert it to char* and then add one.

Francis Glassborow

unread,
Jun 2, 2009, 12:18:57 PM6/2/09
to

I think you are being a bit pedantic. I think that what the OP meant was
that there would be nothing wrong with casting a void* to a double* even
if the void* addresses a block of memory too small for a double as long
as you do not try to dereference the resulting pointer just as there is
nothing wrong with a pointer one beyond the end of an array as long as
you do not try to dereference it.

Keith Thompson

unread,
Jun 2, 2009, 1:19:35 PM6/2/09
to

Well, yes, I'd say that pedantry is appropriate here.

I haven't looked into this in depth, but I think the situation is
something like this:

The pointer returned by malloc (if it's non-null) "is suitably aligned


so that it may be assigned to a pointer to any type of object and then
used to access such an object or an array of such objects in the space

allocated (until the space is explicitly deallocated)". It's possible
that the author(s) of this wording didn't consider the fairly bizarre
case of

double *d = malloc(1); // assuming sizeof(double) > 1

It seems fairly obvious that this is intended to work (and that any
attempt to dereference d invokes undefined behavior), but there may be
some gaps in the wording of the standard. In particular, an argument
based on the permission to construct a pointer one past the end of an
array doesn't seem convincing, because applying it in this case
requires a zero-length array, something that the language doesn't
support.

I don't suggest that the behavior is actually undefined, but it might
be appropriate to tweak the wording of the standard to ensure that it
really is well defined.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Wojtek Lerch

unread,
Jun 2, 2009, 2:03:44 PM6/2/09
to
"Francis Glassborow" <francis.g...@btinternet.com> wrote in message
news:XNednSY-B8VozbjX...@bt.com...

> Wojtek Lerch wrote:
>> "blargg" <blarg...@gishpuppy.com> wrote in message
>> news:blargg.ei3-02...@192.168.1.4...
>>> Couldn't the result of malloc(1) be treated as a past-the-end pointer
>>> for
>>> an array of zero doubles? If so, then (double*)malloc(1)+0 would be
>>> valid.
>>
>> No it couldn't, because there's no such thing as an array of zero doubles
>> in C. Besides, if the result of malloc() were equivalent to a pointer to
>> past the end of any array, you couldn't convert it to char* and then add
>> one.
>
> I think you are being a bit pedantic.

Yeah, I tend to when I'm posting to comp.std.c...

> I think that what the OP meant was that there would be nothing wrong with
> casting a void* to a double* even


Maybe, but wouldn't it have been clearer if he just said that, instead of
referring to the behaviour of something that does not exist in C?

> if the void* addresses a block of memory too small for a double as long as
> you do not try to dereference the resulting pointer just as there is
> nothing wrong with a pointer one beyond the end of an array as long as you
> do not try to dereference it.

The standard guarantees that as long as alignment requirements are
satisfied, any valid pointer (such as the one returned by malloc()) can be
converted to any other pointer type (such as double*) and back. As far as I
can tell, the standard does not say what the converted (double*) pointer
points to, and does not promise that it can be incremented (like a pointer
to an object could) or decremented (like a pointer past the end of an array
could). I don't see how the array analogy helps the discussion.

Vincent Lefevre

unread,
Jun 2, 2009, 7:54:12 PM6/2/09
to
In article <78l7s1F...@mid.individual.net>,
Wojtek Lerch <Wojt...@yahoo.ca> wrote:

> The standard guarantees that as long as alignment requirements are
> satisfied, any valid pointer (such as the one returned by malloc())
> can be converted to any other pointer type (such as double*) and
> back. As far as I can tell, the standard does not say what the
> converted (double*) pointer points to, and does not promise that it
> can be incremented (like a pointer to an object could) or
> decremented (like a pointer past the end of an array could). I don't
> see how the array analogy helps the discussion.

What's more annoying (IMHO) is that it does not guarantee either
that one can add 0 to the pointer. The consequence is that a macro
like

#define NO_LVALUE(X) ((X) + 0)

does not work on all non-null pointers.

Keith Thompson

unread,
Jun 2, 2009, 8:39:29 PM6/2/09
to
Vincent Lefevre <vincen...@vinc17.org> writes:
> In article <78l7s1F...@mid.individual.net>,
> Wojtek Lerch <Wojt...@yahoo.ca> wrote:
>
>> The standard guarantees that as long as alignment requirements are
>> satisfied, any valid pointer (such as the one returned by malloc())
>> can be converted to any other pointer type (such as double*) and
>> back. As far as I can tell, the standard does not say what the
>> converted (double*) pointer points to, and does not promise that it
>> can be incremented (like a pointer to an object could) or
>> decremented (like a pointer past the end of an array could). I don't
>> see how the array analogy helps the discussion.
>
> What's more annoying (IMHO) is that it does not guarantee either
> that one can add 0 to the pointer. The consequence is that a macro
> like
>
> #define NO_LVALUE(X) ((X) + 0)
>
> does not work on all non-null pointers.

If by "does not work" you mean "is not guaranteed by the standard to
work", then I agree. But in practice, I'd be *very* surprised if it
didn't work in practice on some particular implementation.

And since your NO_LVALUE() macro has some other caveats (null pointers
are the most obvious, but there may be others), I suspect the rare
case of

big_type *pointer = malloc(small_size);

isn't worth worrying about in real life.

From a programmer's point of view, there isn't much of a problem.
From an implementer's point of view, it's even easier: unless you go
out of your way to break it, ((X) + 0) is probably just going to work.
But it would be nice if the wording of the standard could be tweaked
to cover this case.

Another question: should (null_pointer + 0) work? (Given the current
wording of the standard, it's undefined behavior; I'm asking whether
it should be defined.) On most systems, it will probably work unless
the compiler goes out of its way to break it; are there systems where
guaranteeing that (null_pointer + 0) yields a null pointer would be a
burden? Does it matter whether the integer operand is constant?

Vincent Lefevre

unread,
Jun 3, 2009, 6:32:20 AM6/3/09
to
In article <lneiu24...@nuthaus.mib.org>,
Keith Thompson <ks...@mib.org> wrote:

> Vincent Lefevre <vincen...@vinc17.org> writes:
> > What's more annoying (IMHO) is that it does not guarantee either
> > that one can add 0 to the pointer. The consequence is that a macro
> > like
> >
> > #define NO_LVALUE(X) ((X) + 0)
> >
> > does not work on all non-null pointers.

> If by "does not work" you mean "is not guaranteed by the standard to
> work", then I agree.

Yes, sorry, this is what I meant.

> But in practice, I'd be *very* surprised if it didn't work in
> practice on some particular implementation.

I'd also be very surprised, but nowadays, with all kinds of
optimizations, one never knows.

> And since your NO_LVALUE() macro has some other caveats (null pointers
> are the most obvious, but there may be others), I suspect the rare
> case of

> big_type *pointer = malloc(small_size);

> isn't worth worrying about in real life.

Yes, maybe, but see below. Also note that one can use the ((X) + 0) in
contexts where one knows that X is not a null pointer.

> From a programmer's point of view, there isn't much of a problem.
> From an implementer's point of view, it's even easier: unless you go
> out of your way to break it, ((X) + 0) is probably just going to work.

But this may be a problem for someone who provides a library, unless
he accepts to restrict the API. And this is not obvious if one doesn't
know how a pointer has been built.

BTW, it's nice to see that the C standard explicitly says that the
pointer p returned by fopen() points to an object:

The fopen function returns a pointer to the object controlling
the stream.

So that it is possible to do p + 0 and p + 1 as long as the file is
open (after the file is closed, evaluating p is undefined behavior
anyway, as said in J.2).

The fopen man page under Linux just says that fopen() returns
"a FILE pointer".

> But it would be nice if the wording of the standard could be tweaked
> to cover this case.

Yes.

> Another question: should (null_pointer + 0) work? (Given the current
> wording of the standard, it's undefined behavior; I'm asking whether
> it should be defined.) On most systems, it will probably work unless
> the compiler goes out of its way to break it; are there systems where
> guaranteeing that (null_pointer + 0) yields a null pointer would be a
> burden? Does it matter whether the integer operand is constant?

I was wondering the same thing...

David R Tribble

unread,
Jun 3, 2009, 10:15:17 AM6/3/09
to
Francis Glassborow wrote:
> I think that what the OP meant was
> that there would be nothing wrong with casting a void* to a double* even
> if the void* addresses a block of memory too small for a double as long
> as you do not try to dereference the resulting pointer just as there is
> nothing wrong with a pointer one beyond the end of an array as long as
> you do not try to dereference it.

That's my understanding, too.

double * dp = malloc(1);

This code will allocate [at least] one byte of storage, suitably
aligned for any data type, return the address of the allocated
memory as a pointer-to-void, and then cast that pointer into
a pointer-to-double and assigned it to dp.

So alignment and pointer datatypes are not the problem.

The problem is that malloc() is not obliged to allocate more
than the requested one byte of storage. But as long as the
pointer is not dereferenced, this will not cause any problems.

So, yes, the code is portable.

I also think that casting the pointer-to-double into a pointer-to-char
and then dereferencing that new pointer is portable:

char * cp = (char *) dp;
*cp = 'x';

-drt

Tim Rentsch

unread,
Jun 3, 2009, 2:56:26 PM6/3/09
to
Keith Thompson <ks...@mib.org> writes:

> some gaps in the wording of the standard. [snip]


>
> I don't suggest that the behavior is actually undefined, but it might
> be appropriate to tweak the wording of the standard to ensure that it
> really is well defined.

What's wrong with the wording that you quoted?

Tim Rentsch

unread,
Jun 3, 2009, 3:08:11 PM6/3/09
to
Keith Thompson <ks...@mib.org> writes:

> [snip]


>
> Another question: should (null_pointer + 0) work? (Given the current
> wording of the standard, it's undefined behavior; I'm asking whether
> it should be defined.) On most systems, it will probably work unless
> the compiler goes out of its way to break it; are there systems where
> guaranteeing that (null_pointer + 0) yields a null pointer would be a
> burden? Does it matter whether the integer operand is constant?

What's the argument for defining it? What does having
it defined buy us?

Keith Thompson

unread,
Jun 3, 2009, 3:27:47 PM6/3/09
to
Tim Rentsch <t...@alumnus.caltech.edu> writes:
> Keith Thompson <ks...@mib.org> writes:
[...]

>> Well, yes, I'd say that pedantry is appropriate here.
>>
>> I haven't looked into this in depth, but I think the situation is
>> something like this:
>>
>> The pointer returned by malloc (if it's non-null) "is suitably aligned
>> so that it may be assigned to a pointer to any type of object and then
>> used to access such an object or an array of such objects in the space
>> allocated (until the space is explicitly deallocated)". It's possible
>> that the author(s) of this wording didn't consider the fairly bizarre
>> case of
>>
>> double *d = malloc(1); // assuming sizeof(double) > 1
>>
>> It seems fairly obvious that this is intended to work (and that any
>> attempt to dereference d invokes undefined behavior), but there may be
>> some gaps in the wording of the standard. [snip]
>>
>> I don't suggest that the behavior is actually undefined, but it might
>> be appropriate to tweak the wording of the standard to ensure that it
>> really is well defined.
>
> What's wrong with the wording that you quoted?

It doesn't guarantee (as far as I can tell) that d + 0 == d. The
definition of pointer+integer addition assumes that the pointer points
to an array, where a single object is treated as a one-element array.
If the allocated space is smaller than one object, there's no array
that can be used to define the semantics of "+".

Keith Thompson

unread,
Jun 3, 2009, 3:38:57 PM6/3/09
to

Not a whole lot.

Upthread, Vincent Lefevre posted the following macro:

#define NO_LVALUE(X) ((X) + 0)

It might be nice if that worked for null pointers. And *if* it
already works on all existing implementations, then tweaking the
standard to require it to work wouldn't be a huge burden.

On the other hand, tweaking the standard like this always runs the
risk of getting it wrong.

I'm not advocating such a change, just asking about the possibility.

Tim Rentsch

unread,
Jun 4, 2009, 1:03:12 PM6/4/09
to
Keith Thompson <ks...@mib.org> writes:

I see.

Personally, I don't have any problem with the idea that the "array
of such objects in the space allocated" might have less than one
element, but I agree that a reasonable reading might draw a
different conclusion.

Related question: Suppose we have

struct abc { int one; int two; int three; };
size_t three_offset = offsetof( struct abc, three );
struct abc *x = calloc( 1, three_offset );
struct abc *y = calloc( 1, sizeof *y + three_offset );
if(x) x->two;
if(y) y[1].two;

Are the last two lines defined behavior or undefined behavior?
Can one be defined and the other be undefined?

I think this issue is discussed in a DR but I don't have
a reference handy. Possibly the answer in that (alleged) DR
has something to say about the "d+0" question (which seems to
me to be the same as the "y+1" question that the last line
brings out).

Tim Rentsch

unread,
Jun 4, 2009, 1:20:58 PM6/4/09
to
Keith Thompson <ks...@mib.org> writes:

> Tim Rentsch <t...@alumnus.caltech.edu> writes:
> > Keith Thompson <ks...@mib.org> writes:
> >> [snip]
> >>
> >> Another question: should (null_pointer + 0) work? (Given the current
> >> wording of the standard, it's undefined behavior; I'm asking whether
> >> it should be defined.) On most systems, it will probably work unless
> >> the compiler goes out of its way to break it; are there systems where
> >> guaranteeing that (null_pointer + 0) yields a null pointer would be a
> >> burden? Does it matter whether the integer operand is constant?
> >
> > What's the argument for defining it? What does having
> > it defined buy us?
>
> Not a whole lot.
>
> Upthread, Vincent Lefevre posted the following macro:
>
> #define NO_LVALUE(X) ((X) + 0)
>
> It might be nice if that worked for null pointers. And *if* it
> already works on all existing implementations, then tweaking the
> standard to require it to work wouldn't be a huge burden.

This proposed motivation strikes me as a weak argument. There
doesn't seem to be much value in having a NO_LVALUE operation,
and even if there were, the implementation proposed here is a
hack -- among other things, it can give a result that has a
different type than does the operand. Saying it doesn't work for
some kinds of pointers (such as any pointer to an incomplete
type), or for null pointers, argues more that the implementation
of NO_LVALUE should be different (ie, a different language
extension) than that adding zero to a null pointer should
be allowed.


> On the other hand, tweaking the standard like this always runs the
> risk of getting it wrong.

The most obvious objection is that allowing zero to be added
to an arbitrary pointer value requires a runtime check for
zero. Having it work only for constant expressions?
Another special case -- that's just as bad, if not worse.


> I'm not advocating such a change, just asking about the possibility.

Understood. An uphill battle, especially in the absence of
any sort of useful result that depends on it.

Keith Thompson

unread,
Jun 4, 2009, 2:51:11 PM6/4/09
to
Tim Rentsch <t...@alumnus.caltech.edu> writes:
> Keith Thompson <ks...@mib.org> writes:
>
>> Tim Rentsch <t...@alumnus.caltech.edu> writes:
>> > Keith Thompson <ks...@mib.org> writes:
>> >> [snip]
>> >>
>> >> Another question: should (null_pointer + 0) work? (Given the current
>> >> wording of the standard, it's undefined behavior; I'm asking whether
>> >> it should be defined.) On most systems, it will probably work unless
>> >> the compiler goes out of its way to break it; are there systems where
>> >> guaranteeing that (null_pointer + 0) yields a null pointer would be a
>> >> burden? Does it matter whether the integer operand is constant?
>> >
>> > What's the argument for defining it? What does having
>> > it defined buy us?
>>
>> Not a whole lot.
>>
>> Upthread, Vincent Lefevre posted the following macro:
>>
>> #define NO_LVALUE(X) ((X) + 0)
>>
>> It might be nice if that worked for null pointers. And *if* it
>> already works on all existing implementations, then tweaking the
>> standard to require it to work wouldn't be a huge burden.
>
> This proposed motivation strikes me as a weak argument.

Agreed.

[...]

>> On the other hand, tweaking the standard like this always runs the
>> risk of getting it wrong.
>
> The most obvious objection is that allowing zero to be added
> to an arbitrary pointer value requires a runtime check for
> zero. Having it work only for constant expressions?
> Another special case -- that's just as bad, if not worse.

On most implementations, no such check would be required; the
generated machine code for pointer arithmetic just treats the pointer
value as a 32-bit or 64-bit integer (signed or unsigned depending on
the machine's memory model). For example, the following program
prints "ok" on my system, and I'd expect it to do so on just about any
other system, even though its behavior is not defined by the standard:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *ptr = NULL;
int zero = 0;
ptr += zero;
if (ptr == NULL) {
puts("ok");
exit(EXIT_SUCCESS);
}
else {
puts("OOPS");
exit(EXIT_FAILURE);
}
}

>> I'm not advocating such a change, just asking about the possibility.
>
> Understood. An uphill battle, especially in the absence of
> any sort of useful result that depends on it.

Here's what might be a slightly stronger argument.

Certain operations happen to work on most or all existing
implementations, but the standard doesn't define their behavior.
Programmers may reasonably (or unreasonably) depend on such operations
working, but they do so at their own risk. In a sense, this means the
standard doesn't quite reflect reality.

Tim Rentsch

unread,
Jun 5, 2009, 12:16:30 AM6/5/09
to
Keith Thompson <ks...@mib.org> writes:

> the machine's memory model). [snip]

Sorry, I guess I wasn't clear enough. I was thinking about the
case of a pointer to an incomplete type:

struct foo;
struct foo *p = 0; // p is a null pointer to an incomplete type
int zero = 0;
p += zero; // this + can be done only if zero == 0
// because otherwise the (sizeof *p) matters


> >> I'm not advocating such a change, just asking about the possibility.
> >
> > Understood. An uphill battle, especially in the absence of
> > any sort of useful result that depends on it.
>
> Here's what might be a slightly stronger argument.
>
> Certain operations happen to work on most or all existing
> implementations, but the standard doesn't define their behavior.
> Programmers may reasonably (or unreasonably) depend on such operations
> working, but they do so at their own risk. In a sense, this means the
> standard doesn't quite reflect reality.

I do think that's a stronger argument. Just how much
stronger depends on (a) how often people rely (or
would rely?) on the non-standard property being true,
and (b) are there implementations out there where
where the non-standard assumption doesn't hold?
I don't have any information on (b); my guess for
(a) is "not very often", but that is just a guess.

Also, the above comments hold only for pointers to
complete types; for pointers to incomplete types,
it seems better that any arithmetic with such
pointers remain a constraint violation.

Vincent Lefevre

unread,
Jun 5, 2009, 7:20:02 PM6/5/09
to
In article <kfnhbyv...@alumnus.caltech.edu>,
Tim Rentsch <t...@alumnus.caltech.edu> wrote:

> Keith Thompson <ks...@mib.org> writes:

> > Upthread, Vincent Lefevre posted the following macro:
> >
> > #define NO_LVALUE(X) ((X) + 0)
> >
> > It might be nice if that worked for null pointers. And *if* it
> > already works on all existing implementations, then tweaking the
> > standard to require it to work wouldn't be a huge burden.

> This proposed motivation strikes me as a weak argument. There
> doesn't seem to be much value in having a NO_LVALUE operation,

Well, for instance, as a particular case, we provide the following
macros in the MPFR library:

#define mpfr_get_prec(_x) ((_x)->_mpfr_prec + 0)
#define mpfr_get_exp(_x) ((_x)->_mpfr_exp + 0)

So, the user can use mpfr_get_prec to get the precision of a MPFR
number, but without the "+ 0", the user could also change the
precision field with the macro (and this would be a bug). So,
the "+ 0" allows the detection of some bugs.

C99's inline functions would probably avoid this kind of tricks,
but software often needs to be built with old compilers (moreover
I'm not sure that inline functions can solve all the problems,
e.g. one may want a type-generic macro).

> and even if there were, the implementation proposed here is a
> hack --

Well, the preprocessor is a hack. :)

> among other things, it can give a result that has a different type
> than does the operand.

The NO_LVALUE macro would typically be used in a context where it
is safe. For instance, the context may imply that the type is a
non-null pointer (not to void). But if you have a non-null double*
pointer, it is not obvious that it necessarily points to a double
object (a good example is the documentation of the glibc for the
fopen function).

> Saying it doesn't work for some kinds of pointers (such as any
> pointer to an incomplete type), or for null pointers, argues more
> that the implementation of NO_LVALUE should be different (ie, a
> different language extension) than that adding zero to a null
> pointer should be allowed.

Unfortunately a user (in particular, someone who provides a library
for other users) cannot assume that all compilers support such a
language extension.

> The most obvious objection is that allowing zero to be added
> to an arbitrary pointer value requires a runtime check for
> zero. Having it work only for constant expressions?

If the operation "+ 0" is allowed because the pointer points to
an object, there would be no change. The only change would be
where the pointer doesn't point to an object; but in that case,
adding any other integer would be undefined behavior. So, an
implementation may safely assume that the integer is always 0
if doing otherwise is a problem.

Vincent Lefevre

unread,
Jun 5, 2009, 7:28:52 PM6/5/09
to
In article <kfneitz...@alumnus.caltech.edu>,
Tim Rentsch <t...@alumnus.caltech.edu> wrote:

> Sorry, I guess I wasn't clear enough. I was thinking about the
> case of a pointer to an incomplete type:

> struct foo;
> struct foo *p = 0; // p is a null pointer to an incomplete type
> int zero = 0;
> p += zero; // this + can be done only if zero == 0
> // because otherwise the (sizeof *p) matters

If zero == 0, then it would be guaranteed that p is not modified.
Here the case zero != 0 would be undefined behavior, so that the
implementation could do anything, in particular let p unchanged
(like in the case zero == 0). So, no additional code would need
to be generated.

> Also, the above comments hold only for pointers to
> complete types; for pointers to incomplete types,
> it seems better that any arithmetic with such
> pointers remain a constraint violation.

Yes, probably.

Wojtek Lerch

unread,
Jun 5, 2009, 7:29:35 PM6/5/09
to
"Vincent Lefevre" <vincen...@vinc17.org> wrote in message
news:20090605224732$6a...@prunille.vinc17.org...

> So, the user can use mpfr_get_prec to get the precision of a MPFR
> number, but without the "+ 0", the user could also change the
> precision field with the macro (and this would be a bug). So,
> the "+ 0" allows the detection of some bugs.

#define NO_LVALUE( x ) ( 0 ? 0 : (x) )

Tim Rentsch

unread,
Jun 6, 2009, 7:30:07 PM6/6/09
to
Vincent Lefevre <vincen...@vinc17.org> writes:

> In article <kfneitz...@alumnus.caltech.edu>,
> Tim Rentsch <t...@alumnus.caltech.edu> wrote:
>
> > Sorry, I guess I wasn't clear enough. I was thinking about the
> > case of a pointer to an incomplete type:
>
> > struct foo;
> > struct foo *p = 0; // p is a null pointer to an incomplete type
> > int zero = 0;
> > p += zero; // this + can be done only if zero == 0
> > // because otherwise the (sizeof *p) matters
>
> If zero == 0, then it would be guaranteed that p is not modified.

That statement makes a presumption about how pointer addition is
implemented. In some implementations, just the act of loading a
null pointer into an address register where it can be operated on
will turn it into not-a-null-pointer. Furthermore such things
aren't that farfetched -- it could very well happen in a
system/360-type architecture implementation, for example.

> Here the case zero != 0 would be undefined behavior, so that the
> implementation could do anything, in particular let p unchanged
> (like in the case zero == 0). So, no additional code would need
> to be generated.

In some cases it wouldn't, but in other cases like the example
mentioned above it would. And that's true both for complete
types and for incomplete types.

Tim Rentsch

unread,
Jun 6, 2009, 8:08:16 PM6/6/09
to
Vincent Lefevre <vincen...@vinc17.org> writes:

> In article <kfnhbyv...@alumnus.caltech.edu>,
> Tim Rentsch <t...@alumnus.caltech.edu> wrote:
>
> > Keith Thompson <ks...@mib.org> writes:
>
> > > Upthread, Vincent Lefevre posted the following macro:
> > >
> > > #define NO_LVALUE(X) ((X) + 0)
> > >
> > > It might be nice if that worked for null pointers. And *if* it
> > > already works on all existing implementations, then tweaking the
> > > standard to require it to work wouldn't be a huge burden.
>
> > This proposed motivation strikes me as a weak argument. There
> > doesn't seem to be much value in having a NO_LVALUE operation,
>
> Well, for instance, as a particular case, we provide the following
> macros in the MPFR library:
>
> #define mpfr_get_prec(_x) ((_x)->_mpfr_prec + 0)
> #define mpfr_get_exp(_x) ((_x)->_mpfr_exp + 0)
>
> So, the user can use mpfr_get_prec to get the precision of a MPFR
> number, but without the "+ 0", the user could also change the
> precision field with the macro (and this would be a bug). So,
> the "+ 0" allows the detection of some bugs.

#define VALUE_OF(x) (0 ? (x) : (x))
#define mpfr_get_prec(x) VALUE_OF((x)->_mpfr_prec)
#define mpfr_get_exp(x) VALUE_OF((x)->_mpfr_exp)

These definitions work just as well or better, and don't rely on
limited operand types or extended semantics.


> C99's inline functions would probably avoid this kind of tricks,
> but software often needs to be built with old compilers (moreover
> I'm not sure that inline functions can solve all the problems,
> e.g. one may want a type-generic macro).

See VALUE_OF() macro, above. Supports all assignable data types,
which NO_LVALUE() does not.


> > and even if there were, the implementation proposed here is a
> > hack --
>
> Well, the preprocessor is a hack. :)

Even if that were true, pointing it out does nothing to strengthen
your argument.


> > among other things, it can give a result that has a different type
> > than does the operand.
>
> The NO_LVALUE macro would typically be used in a context where it
> is safe. For instance, the context may imply that the type is a
> non-null pointer (not to void). But if you have a non-null double*
> pointer, it is not obvious that it necessarily points to a double
> object (a good example is the documentation of the glibc for the
> fopen function).

So, you're admitting that NO_LVALUE() as defined above is
a half-baked solution? That's what it sounds like you're
saying.


> > Saying it doesn't work for some kinds of pointers (such as any
> > pointer to an incomplete type), or for null pointers, argues more
> > that the implementation of NO_LVALUE should be different (ie, a
> > different language extension) than that adding zero to a null
> > pointer should be allowed.
>
> Unfortunately a user (in particular, someone who provides a library
> for other users) cannot assume that all compilers support such a
> language extension.

Maybe this is news to you, but this whole (sub)thread got started
because we are discussing a possible language extension! Not
only that, but based on the comments here, neither language
extension is necessary to do what you want to do.


> > The most obvious objection is that allowing zero to be added
> > to an arbitrary pointer value requires a runtime check for
> > zero. Having it work only for constant expressions?
>
> If the operation "+ 0" is allowed because the pointer points to
> an object, there would be no change. The only change would be
> where the pointer doesn't point to an object; but in that case,
> adding any other integer would be undefined behavior. So, an
> implementation may safely assume that the integer is always 0
> if doing otherwise is a problem.

This comment is basically a repeat of your other recent posting;
the underlying reasoning is flawed, as I explained in my response
to that posting.

For the question of limiting the proposed extension to constant
expressions whose value is zero, the problem is not the runtime
cost but complicating the language specification. Especially
since, as mentioned above, the proposed motivation NO_LVALUE()
is an unnecessary and incomplete "solution" for what is being
done.


Acknowledgment: I give credit to Wojtek Lerch for the macro
definition given in his response, which was the inspiration
for the VALUE_OF() macro above.

Nate Eldredge

unread,
Jun 6, 2009, 9:21:41 PM6/6/09
to
Tim Rentsch <t...@alumnus.caltech.edu> writes:

> Vincent Lefevre <vincen...@vinc17.org> writes:
>> Well, for instance, as a particular case, we provide the following
>> macros in the MPFR library:
>>
>> #define mpfr_get_prec(_x) ((_x)->_mpfr_prec + 0)
>> #define mpfr_get_exp(_x) ((_x)->_mpfr_exp + 0)
>>
>> So, the user can use mpfr_get_prec to get the precision of a MPFR
>> number, but without the "+ 0", the user could also change the
>> precision field with the macro (and this would be a bug). So,
>> the "+ 0" allows the detection of some bugs.
>
> #define VALUE_OF(x) (0 ? (x) : (x))
> #define mpfr_get_prec(x) VALUE_OF((x)->_mpfr_prec)
> #define mpfr_get_exp(x) VALUE_OF((x)->_mpfr_exp)
>
> These definitions work just as well or better, and don't rely on
> limited operand types or extended semantics.

Until you write something like

prec = mpfr_get_prec(*p++);

Tim Rentsch

unread,
Jun 6, 2009, 9:51:52 PM6/6/09
to
Nate Eldredge <na...@vulcan.lan> writes:

If you look again I expect you'll see why this
call doesn't misbehave even though at first
glance it looks like it could.

Keith Thompson

unread,
Jun 6, 2009, 9:56:10 PM6/6/09
to
Tim Rentsch <t...@alumnus.caltech.edu> writes:
> Vincent Lefevre <vincen...@vinc17.org> writes:
>> In article <kfneitz...@alumnus.caltech.edu>,
>> Tim Rentsch <t...@alumnus.caltech.edu> wrote:
>> > Sorry, I guess I wasn't clear enough. I was thinking about the
>> > case of a pointer to an incomplete type:
>>
>> > struct foo;
>> > struct foo *p = 0; // p is a null pointer to an incomplete type
>> > int zero = 0;
>> > p += zero; // this + can be done only if zero == 0
>> > // because otherwise the (sizeof *p) matters
>>
>> If zero == 0, then it would be guaranteed that p is not modified.
>
> That statement makes a presumption about how pointer addition is
> implemented. In some implementations, just the act of loading a
> null pointer into an address register where it can be operated on
> will turn it into not-a-null-pointer. Furthermore such things
> aren't that farfetched -- it could very well happen in a
> system/360-type architecture implementation, for example.

Well, something like this:

struct foo *p0 = NULL;
struct foo *p1 = &some_object;
if (p0 == p1) /* ... */

have to work anyway, so at least *some* pointer operations on null
pointers have to work.

[...]

Nate Eldredge

unread,
Jun 6, 2009, 10:28:44 PM6/6/09
to
Tim Rentsch <t...@alumnus.caltech.edu> writes:

Oops. You're right.

Tim Rentsch

unread,
Jun 7, 2009, 11:58:48 AM6/7/09
to
Keith Thompson <ks...@mib.org> writes:

Certainly what you say is true. But == and != can use
standard machine compare operations (at least, typically
they can), so the same sort of problem doesn't come up
with these operators. It /does/ come up with the relational
operators (<, <=, etc), but in those cases comparing a null
pointer value is always undefined behavior, so that's alright.

Keith Thompson

unread,
Jun 7, 2009, 3:21:01 PM6/7/09
to

They can use standard machine compare operations on typical systems --
but then, loading an address into a register or adding an integer to
it can be done using ordinary integer operations on typical systems.
The question is, are there *atypical* systems where adding 0 to a
pointer is more difficult than comparing a pointer to null. (And I
have no idea what the answer is.)

Tim Rentsch

unread,
Jun 7, 2009, 4:52:57 PM6/7/09
to
Keith Thompson <ks...@mib.org> writes:

Sounds like we're in violent agreement. I think it's more likely
that pointer arithmetic with null values will be costly than it
is that pointer comparison with null values will be costly, but
certainly it's possible that both will be.

Of course, assuming such systems exist, just because pointer
comparison is slow, that doesn't mean the extra cost for doing
pointer arithmetic with null pointers (ie, under the proposed
extension) is irrelevant. If allowing p+0 for null pointers
causes pointer arithmetic to slow down, knowing that pointer
comparison is also slow doesn't change the performance argument
in the pointer arithmetic case.

Herbert Rosenau

unread,
Jun 8, 2009, 1:53:23 PM6/8/09
to
On Sun, 7 Jun 2009 01:56:10 UTC, Keith Thompson <ks...@mib.org> wrote:

> Well, something like this:
>
> struct foo *p0 = NULL;
> struct foo *p1 = &some_object;
> if (p0 == p1) /* ... */
>
> have to work anyway, so at least *some* pointer operations on null
> pointers have to work.
>

No. The only you can do with pointers are

- compare a pointer with 0
- compare a pointer that is NOT a null pointer constant against an
address of an object that is one of the same object
- assign an valid address to it
- only if the pointer is already pointing to a valid object:
- add a number of objects to it
- subtract a number of objects from it
- increment or decrement the pointer
but only if the resulting pointer points to a member of the same
array or
exactly one member after it

undefined behavior is given when
- the pointer is not pointing to a valid object
- comparing 2 pointers whereas one or both are pointing not to the
same object
- comparing 2 pointers using <, <=, >, >=, except both are pointing to
the same object


--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!

Tim Rentsch

unread,
Jun 8, 2009, 2:23:33 PM6/8/09
to
"Herbert Rosenau" <os2...@pc-rosenau.de> writes:

I believe this is a misreading. The discussion in 6.5.9
says nothing that would prohibit comparing a null pointer
value and a non-null pointer value, and 6.5.9 p 6 defines
the result of such a comparison clearly and unambiguously.
So the 'if(p0==p1)' above is defined behavior.

Keith Thompson

unread,
Jun 8, 2009, 3:02:20 PM6/8/09
to
"Herbert Rosenau" <os2...@pc-rosenau.de> writes:
> On Sun, 7 Jun 2009 01:56:10 UTC, Keith Thompson <ks...@mib.org> wrote:
>> Well, something like this:
>>
>> struct foo *p0 = NULL;
>> struct foo *p1 = &some_object;
>> if (p0 == p1) /* ... */
>>
>> have to work anyway, so at least *some* pointer operations on null
>> pointers have to work.
>>
>
> No. The only you can do with pointers are
[snip]

I believe you're mistaken. Take a look at C99 6.5.9. The comparison
(p0 == p1) satisfies the constraint:

both operands are pointers to qualified or unqualified versions of
compatible types

As for the semantics:

Two pointers compare equal if and only if both are null pointers,
both are pointers to the same object (including a pointer to an
object and a subobject at its beginning)
[... snip further conditions not relevant to the example]

So the two pointers compare unequal. There's no undefined behavior.

The restriction on pointing to or into the same object applies to the
relational operators, not to the equality operators. The behavior of
(p0 < p1) would be undefined; the behavior of (p0 == p1) is not.

Vincent Lefevre

unread,
Jun 14, 2009, 8:57:51 PM6/14/09
to
In article <78to30F...@mid.individual.net>,
Wojtek Lerch <Wojt...@yahoo.ca> wrote:

If x is a pointer, I don't think that's allowed. But Tim's solution
seems to be OK (let's just hope that the compiler won't emit warning
messages).

Vincent Lefevre

unread,
Jun 14, 2009, 9:52:59 PM6/14/09
to
In article <kfnprdg...@alumnus.caltech.edu>,
Tim Rentsch <t...@alumnus.caltech.edu> wrote:

> #define VALUE_OF(x) (0 ? (x) : (x))
> #define mpfr_get_prec(x) VALUE_OF((x)->_mpfr_prec)
> #define mpfr_get_exp(x) VALUE_OF((x)->_mpfr_exp)

> These definitions work just as well or better, and don't rely on
> limited operand types or extended semantics.

Yes, I hadn't thought about this solution. Thank you (and Wojtek)
very much.

> > > The most obvious objection is that allowing zero to be added
> > > to an arbitrary pointer value requires a runtime check for
> > > zero. Having it work only for constant expressions?
> >
> > If the operation "+ 0" is allowed because the pointer points to
> > an object, there would be no change. The only change would be
> > where the pointer doesn't point to an object; but in that case,
> > adding any other integer would be undefined behavior. So, an
> > implementation may safely assume that the integer is always 0
> > if doing otherwise is a problem.

> This comment is basically a repeat of your other recent posting;
> the underlying reasoning is flawed, as I explained in my response
> to that posting.

OK, I was assuming a system where one can add a value to any pointer.
However I suspect that advanced compiler optimizations would handle
most practical cases.

Wojtek Lerch

unread,
Jun 14, 2009, 10:26:24 PM6/14/09
to

"Vincent Lefevre" <vincen...@vinc17.org> wrote in message
news:20090615005539$3d...@prunille.vinc17.org...

> In article <78to30F...@mid.individual.net>,
> Wojtek Lerch <Wojt...@yahoo.ca> wrote:
>
>> #define NO_LVALUE( x ) ( 0 ? 0 : (x) )
>
> If x is a pointer, I don't think that's allowed. But Tim's solution
> seems to be OK (let's just hope that the compiler won't emit warning
> messages).

It's allowed because 0 is a null pointer constant. But I prefer Tim's
(0?(x):(x)) too.

David R Tribble

unread,
Jun 15, 2009, 7:42:18 PM6/15/09
to
Vincent Lefevre wrote:
>> Well, for instance, as a particular case, we provide the following
>> macros in the MPFR library:
>>
>> #define mpfr_get_prec(_x) ((_x)->_mpfr_prec + 0)
>> #define mpfr_get_exp(_x) ((_x)->_mpfr_exp + 0)
>>
>> So, the user can use mpfr_get_prec to get the precision of a MPFR
>> number, but without the "+ 0", the user could also change the
>> precision field with the macro (and this would be a bug). So,
>> the "+ 0" allows the detection of some bugs.
>

Wojtek Lerch wrote:
> #define NO_LVALUE( x ) ( 0 ? 0 : (x) )

I was going to suggest:
#define mpfr_get_prec(_x) (+(_x)->_mpfr_prec)
#define mpfr_get_exp(_x) (+(_x)->_mpfr_exp)

The unary '+' operator would appear to be safer than
the binary '+' operator within the context of this discussion.

-drt

Wojtek Lerch

unread,
Jun 15, 2009, 10:22:06 PM6/15/09
to
"David R Tribble" <da...@tribble.com> wrote in message
news:3b69a0f8-d440-4a53...@s12g2000yqi.googlegroups.com...

> Vincent Lefevre wrote:
>>> Well, for instance, as a particular case, we provide the following
>>> macros in the MPFR library:
>>>
>>> #define mpfr_get_prec(_x) ((_x)->_mpfr_prec + 0)
>>> #define mpfr_get_exp(_x) ((_x)->_mpfr_exp + 0)
>
> Wojtek Lerch wrote:
>> #define NO_LVALUE( x ) ( 0 ? 0 : (x) )
>
> I was going to suggest:
> #define mpfr_get_prec(_x) (+(_x)->_mpfr_prec)
> #define mpfr_get_exp(_x) (+(_x)->_mpfr_exp)
>
> The unary '+' operator would appear to be safer than
> the binary '+' operator within the context of this discussion.

I guess there were two discussions here, or at least two contexts. The
original NO_LVALUE() macro was meant to turn an lvalue of any scalar type
into an rvalue (but failed to do its job when the value was a null pointer,
or possibly some other valid pointer values). The mpfr... macros were meant
to just return the value stored in a particular structure member, as a
rvalue, rather than an lvalue, of the appropriate type. The unary '+' works
for arithmetic types, but not for pointer types, and therefore is
appropriate in the second context (assuming those members were indeed
arithmetic) but not the first.

Tim Rentsch

unread,
Jun 19, 2009, 7:48:31 PM6/19/09
to
Vincent Lefevre <vincen...@vinc17.org> writes:

I agree that as a practical matter allowing 0 to be added to null
pointers won't add any execution burden on most systems. My
point is that, when considering a change or extension to the
Standard, one needs to consider the effect on all systems, not
just most, and that adding some descriptive complexity is also a
cost. I don't see anything wrong with an implementation saying
it allows 0 to be added to null pointers, and nothing bad will
happen; I just don't think such a requirement should be imposed
on all implementations, both because of unforseen implementation
costs (in some cases) and because of additional descriptive
complexity. I see costs that are non-trivial, but I don't
see any non-trivial benefits.

0 new messages