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

Casts on lvalues

151 views
Skip to first unread message

BartC

unread,
Dec 2, 2012, 8:23:39 AM12/2/12
to
Suppose I have these types:

#define byte unsigned char

typedef struct {
int a,b,c,d;
} R; /* assume this is 16 bytes */

And these variables:

int n; /* represents a *byte* offset */
R* p;

I want to be able to do the following:
p += n;

but it doesn't work because n is a byte offset; it's not in terms of R
objects. But the obvious cast:

(byte*)p+=n;

doesn't appear to compile. The workarounds seem to be:

p += n/sizeof(R);

which I don't like. Even though p is always aligned (and n is known to be a
multiple of 16), and I know the divide will cancel out, it seems funny
having to introduce a divide op in the first place. And:

p = (R*)((byte*)p+n);

which is what I'm using but looks very untidy in real code.

In any case, the question remains, *is* there a way to cast an lvalue as
I've shown above?

--
Bartc

SG

unread,
Dec 2, 2012, 8:40:52 AM12/2/12
to
In C++ there is. But you would invoke undefined behaviour because doing
it in this case violates the aliasing rule. I really don't see the
problem with

p += n/sizeof(R);

It looks like exactly the thing you should write. Maybe throw an

assert(n % sizeof(R) == 0);

in there as well.

Ben Bacarisse

unread,
Dec 2, 2012, 8:53:21 AM12/2/12
to
No, I don't think so. Certainly not directly -- a cast expression is
not a lvalue. You can do dangerous thing like:

*(byte **)&p += n; /* don't do this!! */

or use a union with an R * and a char * pointer in it, but both
techniques rely on the representation of char and struct pointers being
the same -- they re-interpret the pointer rather than converting it.

I think your best bet is to tidy up what you currently use

static inline void *addr_inc(void *p, int n) { return (char *)p + n; }

will let you write p = addr_inc(p, n); which is much less messy and not
hard to follow.

--
Ben.

Keith Thompson

unread,
Dec 2, 2012, 3:34:16 PM12/2/12
to
"BartC" <b...@freeuk.com> writes:
> Suppose I have these types:
>
> #define byte unsigned char

Why are you using a macro rather than a typedef?

> typedef struct {
> int a,b,c,d;
> } R; /* assume this is 16 bytes */

Or you could assume that it's `sizeof R` bytes.

> And these variables:
>
> int n; /* represents a *byte* offset */
> R* p;
>
> I want to be able to do the following:
> p += n;
>
> but it doesn't work because n is a byte offset; it's not in terms of R
> objects. But the obvious cast:
>
> (byte*)p+=n;
>
> doesn't appear to compile.

Right. A cast operator doesn't require an lvalue as its operand.
If the operand happens to be an lvalue, it's not treated as one.
It undergoes an "lvalue conversion" as described in N1570 6.3.2.1p2.
The result of a cast is not an lvalue.

> The workarounds seem to be:
>
> p += n/sizeof(R);
>
> which I don't like. Even though p is always aligned (and n is known to be a
> multiple of 16), and I know the divide will cancel out, it seems funny
> having to introduce a divide op in the first place. And:
>
> p = (R*)((byte*)p+n);
>
> which is what I'm using but looks very untidy in real code.
>
> In any case, the question remains, *is* there a way to cast an lvalue as
> I've shown above?

No.

Why is n a byte offset in the first place? You're dealing with
objects of your anonymous struct type; can't you just give your
struct a name and make p a pointer to it? If you need a byte
pointer, you can convert a struct pointer to byte*.

Incidentally, I'd probably use unsigned char directly; it's clear enough
and doesn't make the reader wonder how "byte" has been defined.

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

BartC

unread,
Dec 2, 2012, 4:18:09 PM12/2/12
to
"Keith Thompson" <ks...@mib.org> wrote in message
news:lny5hg8...@nuthaus.mib.org...
> "BartC" <b...@freeuk.com> writes:

>> int n; /* represents a *byte* offset */
>> R* p;
>>
>> I want to be able to do the following:
>> p += n;
>>
>> but it doesn't work because n is a byte offset; it's not in terms of R
>> objects. But the obvious cast:

> Why is n a byte offset in the first place?

The offsets come from externally generated data, and initially were simple
counts: +2, -1 etc. The code looked like this:

R *p,*q;

p=q+n;

Very nice. Then I noticed this addition involved internally multiplying n by
16 (or a shift as it was), which wasn't so nice! It was just as easy to
generate these numbers as multiples of 16 anyway (+32, -16 etc) so I did
that. But then the code wasn't so pretty.

--
Bartc

pete

unread,
Dec 2, 2012, 4:21:09 PM12/2/12
to
Keith Thompson wrote:

> The result of a cast is not an lvalue.

I don't think that the standard explicitly states this;
but as far as I can tell,
the result of any conversion of the type of any expression,
is not an lvalue.

--
pete

Eric Sosman

unread,
Dec 2, 2012, 5:10:04 PM12/2/12
to
On 12/2/2012 4:21 PM, pete wrote:
> Keith Thompson wrote:
>
>> The result of a cast is not an lvalue.
>
> I don't think that the standard explicitly states this;

It's in a footnote to 6.5.4p5. Footnotes are non-normative,
but then there's the definition of lvalue in 6.3.2.1p1:

"An lvalue is an expression [...] that potentially
designates an object [...]"

There is no cast that produces an object designator,[*] hence
there is no cast that produces an lvalue.

[*] You can (sometimes) derive an lvalue from the result
of a cast by applying an operator to it, as in

* (unsigned char*)&foo = 42;

or
((struct baz*)&foo) -> mumble = 17;

> but as far as I can tell,
> the result of any conversion of the type of any expression,
> is not an lvalue.

Even a non-conversion is not an lvalue:

double trouble;
(double)trouble = 3.14; // BZZZT!

--
Eric Sosman
eso...@comcast-dot-net.invalid

pete

unread,
Dec 2, 2012, 6:50:53 PM12/2/12
to
The other type conversion that I was thinking of,
was the implicit conversion of an array type.

--
pete

James Kuyper

unread,
Dec 2, 2012, 7:14:08 PM12/2/12
to
The standard says that something is not an lvalue in only a few places:
6.3.2.1p3 when an lvalue of array type is converted to a pointer to its
first element.
6.5.2.3p3 function_returning_struct_type().member
6.5.16p3 assignment expression

Most of the time, it only says when something IS an lvalue:
6.5.1p2 object_identifier
6.5.1p4 "string literal"
6.5.1p5 (lvalue)
6.5.1.1p4 _Generic(x, int:lvalue1, default:lvalue2)
6.5.2.3p3 lvalue.member
6.5.2.3p4 expression->member
6.5.2.5p4 compound literal
6.5.3.4p4 *pointer_to_object
6.9.1p9 function parameter identifier
--
James Kuyper

Edward A. Falk

unread,
Dec 4, 2012, 6:57:58 PM12/4/12
to
In article <xDIus.482873$GX.3...@fx01.am4>, BartC <b...@freeuk.com> wrote:
>
>I want to be able to do the following:
> p += n;
>
>but it doesn't work because n is a byte offset; it's not in terms of R
>objects. But the obvious cast:
>
> (byte*)p+=n;
>
>doesn't appear to compile. The workarounds seem to be:
>
> p += n/sizeof(R);

Other options:

p += n/sizeof(*p);

p = (R *)((byte *)p +n)

But frankly, none of these is a very good choice. If I were
code-reviewing something like this, I'd ask the programmer what it is
that they're *really* trying to do. Constructs like this are
a likely sign of broken logic and broken code.

Note also that the n/sizeof(...) variant fails horribly if the
byte offset isn't a multiple of the structure size.

Not to say that something like this should *never* be written --
I can think of a number of cases where multiple data structures
might be packed into a buffer, in which case this is exactly the
sort of thing you might be doing. But in cases like this, you're
best off keeping p as a byte pointer and just casting it to a
pointer to R when you need to dereference it.

I'm assuming you're taking the proper precautions w.r.t. alignment
issues. That's a whole 'nother discussion.

--
-Ed Falk, fa...@despams.r.us.com
http://thespamdiaries.blogspot.com/

BartC

unread,
Dec 4, 2012, 8:35:08 PM12/4/12
to
"Edward A. Falk" <fa...@rahul.net> wrote in message
news:k9m2m6$vfu$1...@blue-new.rahul.net...
> In article <xDIus.482873$GX.3...@fx01.am4>, BartC <b...@freeuk.com> wrote:
>>
>>I want to be able to do the following:
>> p += n;
>>
>>but it doesn't work because n is a byte offset; it's not in terms of R

> Other options:
>
> p += n/sizeof(*p);
>
> p = (R *)((byte *)p +n)
>
> But frankly, none of these is a very good choice. If I were
> code-reviewing something like this, I'd ask the programmer what it is
> that they're *really* trying to do. Constructs like this are
> a likely sign of broken logic and broken code.

I've already said elsewhere that using an object rather than a byte offset
involved an unnecessary multiplication or shift.

I noticed it when I wanted shadow some functions with assembly code.

--
Bartc

Piotr Kalinowski

unread,
Dec 5, 2012, 10:07:40 AM12/5/12
to
"BartC" <b...@freeuk.com> writes:

> "Edward A. Falk" <fa...@rahul.net> wrote in message
> news:k9m2m6$vfu$1...@blue-new.rahul.net...
> I've already said elsewhere that using an object rather than a byte
> offset involved an unnecessary multiplication or shift.

It was not unnecessary. It's called pointer arithmetic and allowed you
to use more elegant code at slightly higher level of abstraction that
is supposed to more closely represent your intentions.

You have decided this abstraction is unnecessary, because you feel you
need to optimise this shift/multiplication away (and maybe you do, I
couldn't possibly know that). More specifically, you have implicitly
decided this optimisation is worth the trade off in form of increased
complexity of the code (decreased readability or whatever it is that
bugged you enough to actually ask about it, seeking a different way to
express what you are doing).

There's no free lunch. Now deal with the consequences, or revert your
decision, as others already presented answer to your original question.

Regards,
Piotr Kalinowski

BartC

unread,
Dec 5, 2012, 11:47:58 AM12/5/12
to


"Piotr Kalinowski" <pit...@gmail.com> wrote in message
news:m2sj7km...@gmail.com...
> "BartC" <b...@freeuk.com> writes:
>
>> I've already said elsewhere that using an object rather than a byte
>> offset involved an unnecessary multiplication or shift.
>
> It was not unnecessary. It's called pointer arithmetic and allowed you
> to use more elegant code at slightly higher level of abstraction that
> is supposed to more closely represent your intentions.

Yet, C allows to you to do this - cast the target of a pointer - in an
rvalue expression.

> You have decided this abstraction is unnecessary, because you feel you
> need to optimise this shift/multiplication away (and maybe you do, I
> couldn't possibly know that). More specifically, you have implicitly
> decided this optimisation is worth the trade off in form of increased
> complexity of the code (decreased readability or whatever it is that
> bugged you enough to actually ask about it, seeking a different way to
> express what you are doing).

I asked about doing the same on the left side of an assignment.

It's this asymmetry that is the issue.

--
Bartc

Eric Sosman

unread,
Dec 5, 2012, 1:04:19 PM12/5/12
to
On 12/5/2012 11:47 AM, BartC wrote:
>
> Yet, C allows to you to do this - cast the target of a pointer - in an
> rvalue expression.

No: C allows you to apply a cast operator to a *value* (which
may have been extracted from a pointer's target), not to the target
itself.

>[...]
> I asked about doing the same on the left side of an assignment.
>
> It's this asymmetry that is the issue.

Um, er, assignment itself is asymmetric ...

--
Eric Sosman
eso...@comcast-dot-net.invalid

BartC

unread,
Dec 5, 2012, 1:39:34 PM12/5/12
to


"Eric Sosman" <eso...@comcast-dot-net.invalid> wrote in message
news:k9o2b3$bcb$1...@dont-email.me...
> On 12/5/2012 11:47 AM, BartC wrote:
>>
>> Yet, C allows to you to do this - cast the target of a pointer - in an
>> rvalue expression.
>
> No: C allows you to apply a cast operator to a *value* (which
> may have been extracted from a pointer's target), not to the target
> itself.

Take this example:

typedef unsigned char byte;

int a = 0x12345678;
int* p = &a;

printf( "*p = %X\n", *p);
printf( "*p = %X\n", *(byte*)p);

In the last line, p is made to behave as though it was a byte pointer,
rather than an int pointer. And quite likely, a byte value is requested from
memory rather than an int one. Also, in most cases, the value of the pointer
need not be changed.

The effect is to change the target of the pointer *type*, rather than any
actual value.

>>[...]
>> I asked about doing the same on the left side of an assignment.
>>
>> It's this asymmetry that is the issue.
>
> Um, er, assignment itself is asymmetric ...

Plenty of terms can appear interchangeably on both left and right sides.

But while A can appear on either side, (T)A can't. And there doesn't appear
to be a convincing reason why not.

--
Bartc

James Kuyper

unread,
Dec 5, 2012, 2:23:01 PM12/5/12
to
On 12/05/2012 01:39 PM, BartC wrote:
...
>> On 12/5/2012 11:47 AM, BartC wrote:
...
>>> I asked about doing the same on the left side of an assignment.
...
> But while A can appear on either side, (T)A can't. And there doesn't appear
> to be a convincing reason why not.

The reason is the same as the one that Keith gave to the similar
question you asked at the start of this thread about compound assignment
expressions: "The result of a cast is not an lvalue." Since it's not an
lvalue, no memory is set aside to store anything in it. It appears that
you want

(T)A op= B

to have the meaning that can currently be expressed in standard C as

A = (T)A op B.

but it's not clear to me what you want

(T)A = B

to mean. Can you express it in terms of standard C, the way that I did
above for op=? Consider:

int a=3;
(double)a = 3.14;

Currently, this is equivalent to

3.0 = 3.14;

and therefore disallowed for the same reason that 3.0 = 3.14 is. What
would you like it to do in a revised version of C?


Eric Sosman

unread,
Dec 5, 2012, 2:43:46 PM12/5/12
to
On 12/5/2012 1:39 PM, BartC wrote:
>
>
> "Eric Sosman" <eso...@comcast-dot-net.invalid> wrote in message
> news:k9o2b3$bcb$1...@dont-email.me...
>> On 12/5/2012 11:47 AM, BartC wrote:
>>>
>>> Yet, C allows to you to do this - cast the target of a pointer - in an
>>> rvalue expression.
>>
>> No: C allows you to apply a cast operator to a *value* (which
>> may have been extracted from a pointer's target), not to the target
>> itself.
>
> Take this example:
>
> typedef unsigned char byte;
>
> int a = 0x12345678;
> int* p = &a;
>
> printf( "*p = %X\n", *p);
> printf( "*p = %X\n", *(byte*)p);
>
> In the last line, p is made to behave as though it was a byte pointer,
> rather than an int pointer. And quite likely, a byte value is requested
> from memory rather than an int one. Also, in most cases, the value of
> the pointer need not be changed.

No: `p' is not "made to behave" like anything other than
what it is. `p' is an identifier, which (in this context) is
seen first as a primary expression (6.5.1p2), and then as an
lvalue (6.3.2.1p1). The object designated by this lvalue is
consulted to obtain a value (6.3.2.1p2); this value points at
the object designated by `a' and has the type `int*'.

The `(byte*)' operator converts the extracted value to the
`(unsigned char*)' type (6.5.4), and this converted value points
at the lowest-addressed constituent byte of the object designated
by `a' (6.3.2.3p7).

The `*' operator uses the `(unsigned char*)' value produced
by the cast to designate the pointed-to byte (6.5.3.2p4), and
this designation is an lvalue (6.3.2.1p1 again), from which is
extracted the value stored in the designated byte (6.3.2.1p2
again), which is a value of type `unsigned char'. The default
argument promotions convert this value to `int' or `unsigned int',
and lo! we've computed an argument for printf().

`p' is unchanged throughout all of this. Its value has not
changed, its behavior has not changed, its nature has not changed.
It has not put on makeup, adopted a foreign accent, or affected a
silly walk. It is still an `int*' aimed at the object designated
by `a'.

> The effect is to change the target of the pointer *type*, rather than
> any actual value.

No. See above -- or ask "Has the type of `p's target changed?"
Since `p's target is exactly what it was to begin with, and has made
no sorties into strange territory and back again, the answer is "No."
There has been no change, hence "the effect is to change" is wrong.

>>> [...]
>>> I asked about doing the same on the left side of an assignment.
>>>
>>> It's this asymmetry that is the issue.
>>
>> Um, er, assignment itself is asymmetric ...
>
> Plenty of terms can appear interchangeably on both left and right sides.
>
> But while A can appear on either side, (T)A can't. And there doesn't
> appear to be a convincing reason why not.

If you're not convinced that an lvalue is different from other
kinds of expressions, you're beyond my power to educate -- but that's
nothing new.

--
Eric Sosman
eso...@comcast-dot-net.invalid

Greg Martin

unread,
Dec 5, 2012, 3:25:23 PM12/5/12
to
Since you aren't actually changing A by (T)A but rather saying treat
it's value like it's a T rather then A's declared type, it doesn't
really make sense. I can't see the value of momentarily treating A like
it's a T just to fool the compiler. Presuming T is larger then A are you
asking the compiler to overwrite the memory adjacent to A? Probably not.
Your probably asking it to cast the value to A's type which is more
logically written A = (A_Type) some_T; // IMO.

Excuse me if I've missed your meaning but I think if you think about
what a cast is you'll see it doesn't apply to lvalues.





Ben Bacarisse

unread,
Dec 5, 2012, 5:07:31 PM12/5/12
to
"BartC" <b...@freeuk.com> writes:

> "Eric Sosman" <eso...@comcast-dot-net.invalid> wrote in message
> news:k9o2b3$bcb$1...@dont-email.me...
<snip>
>> Um, er, assignment itself is asymmetric ...
>
> Plenty of terms can appear interchangeably on both left and right sides.
>
> But while A can appear on either side, (T)A can't. And there doesn't
> appear to be a convincing reason why not.

B = (T)A; // OK
(T)A = B; // not OK
B = +A; // OK
+A = B; // not OK
B = !A; // OK
!A = B; // not OK
B = A + C; // OK
A + C = B; // not OK

... and so on. There's a pattern.

--
Ben.

BartC

unread,
Dec 5, 2012, 4:57:03 PM12/5/12
to
"Greg Martin" <gr...@softsprocket.com> wrote in message
news:U4Ovs.586$ww5...@newsfe01.iad...
> On 12-12-05 10:39 AM, BartC wrote:

>> Plenty of terms can appear interchangeably on both left and right sides.
>>
>> But while A can appear on either side, (T)A can't. And there doesn't
>> appear to be a convincing reason why not.
>>
>
> Since you aren't actually changing A by (T)A but rather saying treat it's
> value like it's a T rather then A's declared type, it doesn't really make
> sense.

Isn't that the point of having casts?

> I can't see the value of momentarily treating A like it's a T just to fool
> the compiler.

On a platform that you know inside-out, and where the alternative is to do
exactly the same but using assembly code with all of it's disadvantages,
there there is plenty of value in doing it.

When you have a datablock of mixed-type data, and traversing it via a
pointer, then you might expect to switch pointer types all the time, rather
than messing with unions and memcpys.

> Presuming T is larger then A are you asking the compiler to overwrite the
> memory adjacent to A? Probably not.

Yes. But you'd be sensible enough not to do that.

> Your probably asking it to cast the value to A's type which is more
> logically written A = (A_Type) some_T; // IMO.

That's not quite the same. See the example I gave in my reply to James.

--
Bartc

BartC

unread,
Dec 5, 2012, 5:11:57 PM12/5/12
to
"James Kuyper" <james...@verizon.net> wrote in message
news:50BF9F15...@verizon.net...
> On 12/05/2012 01:39 PM, BartC wrote:

>> But while A can appear on either side, (T)A can't. And there doesn't
>> appear
>> to be a convincing reason why not.
>
> The reason is the same as the one that Keith gave to the similar
> question you asked at the start of this thread about compound assignment
> expressions: "The result of a cast is not an lvalue."

OK, I get that now. But that's only because the Book says so.

>It appears that you want
>
> (T)A op= B
>
> to have the meaning that can currently be expressed in standard C as
>
> A = (T)A op B.

It's not quite that either, because this might involve unwanted int/float
conversions.

> but it's not clear to me what you want
>
> (T)A = B
>
> to mean. Can you express it in terms of standard C, the way that I did
> above for op=?

It means: "pretend that A is a variable of type T for this assignment".

> Consider:
>
> int a=3;
> (double)a = 3.14;
>
> Currently, this is equivalent to
>
> 3.0 = 3.14;
>
> and therefore disallowed for the same reason that 3.0 = 3.14 is. What
> would you like it to do in a revised version of C?

Well in this case it wouldn't do anything too useful! But turning it around
a little:

double a;

(int)a = 3142;

This just writes the bit-pattern for integer 3142 in (on my machine) the
bottom half of a. But that, I can currently do in C using instead:

*(int*)&a = 3142;

(even though people don't seem to like this either). My original example was
more like this:

int* P;

++(char*)P;

ie. treat P as as a char* pointer (so that on my machine, the value in P
increments by 1 instead of 4).

I used to do this stuff in another language something like this:

double a;
int n;
equivalence(a,n) /* a and n share the same memory location */

n=3142; /* does what I tried to do above */

OK, I understand in C, you have to use unions and memcpys and things. But
that's not always straightforward (for example, 'a' might be an extern
variable, or part of an array).


--
Bartc

BartC

unread,
Dec 5, 2012, 5:20:45 PM12/5/12
to


"Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
news:0.9fd76eb692e0db06bfc4.2012...@bsb.me.uk...
OK, I see it. But: the (T)A=B example might be done instead as:

memcpy(&A, &B, sizeof(T));

So it expresses something that could conceivably make sense. Unlike the
other examples that don't!

--
Bartc

Willem

unread,
Dec 5, 2012, 5:36:50 PM12/5/12
to
BartC wrote:
) The offsets come from externally generated data, and initially were simple
) counts: +2, -1 etc. The code looked like this:
)
) R *p,*q;
)
) p=q+n;
)
) Very nice. Then I noticed this addition involved internally multiplying n by
) 16 (or a shift as it was), which wasn't so nice! It was just as easy to
) generate these numbers as multiples of 16 anyway (+32, -16 etc) so I did
) that. But then the code wasn't so pretty.

That's quite odd. A good compiler should have done that optimization for
you, if it's at all possible. Also, on x86 CPU's, there are addressing
modes that implicitly multiply by factors of two, with no speed penalty.

And, obviously, this is pretty much a micro optimization. Are you that
concerned with execution speed? Is this for some embedded cpu?


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT

Ian Collins

unread,
Dec 5, 2012, 5:51:06 PM12/5/12
to
BartC wrote:
>
> My original example was
> more like this:
>
> int* P;
>
> ++(char*)P;
>
> ie. treat P as as a char* pointer (so that on my machine, the value in P
> increments by 1 instead of 4).

Which may well result in P not being a valid pointer to an int, ending
the world when it is dereferenced. Such a construct is like giving a 3
year old a box ox matches. C does have plenty of matches on offer, but
they are normally in child proof boxes.

If you want a pointer to walk through some data built from an arbitrary
set of types, use a char*.

--
Ian Collins

BartC

unread,
Dec 5, 2012, 6:46:16 PM12/5/12
to


"Willem" <wil...@turtle.stack.nl> wrote in message
news:slrnkbvj42...@turtle.stack.nl...
> BartC wrote:
> ) The offsets come from externally generated data, and initially were
> simple
> ) counts: +2, -1 etc. The code looked like this:
> )
> ) R *p,*q;
> )
> ) p=q+n;
> )
> ) Very nice. Then I noticed this addition involved internally multiplying
> n by
> ) 16 (or a shift as it was), which wasn't so nice! It was just as easy to
> ) generate these numbers as multiples of 16 anyway (+32, -16 etc) so I did
> ) that. But then the code wasn't so pretty.
>
> That's quite odd. A good compiler should have done that optimization for
> you, if it's at all possible. Also, on x86 CPU's, there are addressing
> modes that implicitly multiply by factors of two, with no speed penalty.

How can it be optimised? If the pointer has a 16-byte stride, and you're
adding an offset N, then N*16 must be added to the pointer. The x86's
address scaling feature only goes up to *8. This *16 is implemented with a
shift.

> And, obviously, this is pretty much a micro optimization. Are you that
> concerned with execution speed? Is this for some embedded cpu?

As it happened, it made very little difference (about 0.4% better), even
though this unnecessary operation was executed some 80 million times every
second. But sometimes you are just making dozens of tiny improvements which
together make a worthwhile difference. (Sometimes, they inexplicably make
things slower too.)

But I just don't like the idea of my code (and the instruction cache)
getting cluttered up with things that don't need to be there. I doubt they
will make it go faster!

(And yes I am hoping to get this working on a slow-running board computer --
one day.)

--
Bartc

BartC

unread,
Dec 5, 2012, 6:58:27 PM12/5/12
to


"Ian Collins" <ian-...@hotmail.com> wrote in message
news:aia1eq...@mid.individual.net...
> BartC wrote:
>>
>> My original example was
>> more like this:
>>
>> int* P;
>>
>> ++(char*)P;
>>
>> ie. treat P as as a char* pointer (so that on my machine, the value in P
>> increments by 1 instead of 4).
>
> Which may well result in P not being a valid pointer to an int, ending the
> world when it is dereferenced. Such a construct is like giving a 3 year
> old a box ox matches. C does have plenty of matches on offer, but they
> are normally in child proof boxes.

But, I can do the equivalent of ++(char*)P by writing:

P = (int*)((char*)P+1);

(or near enough if that's not quite right.) So the world can still end.

--
Bartc

Keith Thompson

unread,
Dec 5, 2012, 7:09:23 PM12/5/12
to
"BartC" <b...@freeuk.com> writes:
> "Greg Martin" <gr...@softsprocket.com> wrote in message
> news:U4Ovs.586$ww5...@newsfe01.iad...
>> On 12-12-05 10:39 AM, BartC wrote:
>>> Plenty of terms can appear interchangeably on both left and right sides.
>>>
>>> But while A can appear on either side, (T)A can't. And there doesn't
>>> appear to be a convincing reason why not.
>>
>> Since you aren't actually changing A by (T)A but rather saying treat it's
>> value like it's a T rather then A's declared type, it doesn't really make
>> sense.
>
> Isn't that the point of having casts?

No. The point of a cast is to explicitly convert a *value* of some
type to a value of some other specified type.

There are several ways in C to treat an object of some type as if it
were an object of some other type. A simple cast is not one of them.

Depending on what you're trying to do, you can take the object's
address, cast it to some other pointer type, and then dereference the
result. You do so at your own risk; it can fail badly if the object
doesn't have the proper alignment for the target type, or if the target
type is larger than the object, or if you assign an invalid value (such
as a trap representation) to the object.

Or you can use a union; this avoids alignment issues, but the other
problems still apply.

Or you can use memcpy() to create a *copy* of an object's representation
in an object of a different type.

>> I can't see the value of momentarily treating A like it's a T just to fool
>> the compiler.
>
> On a platform that you know inside-out, and where the alternative is to do
> exactly the same but using assembly code with all of it's disadvantages,
> there there is plenty of value in doing it.

There are *plenty* of ways to do what you want to do without
resorting to assembly code. It's true that they're not as
syntactically "clean" as a hypothetical lvalue cast. Personally,
I think that's a good thing; type-punning is dangerous enough that
it shouldn't be done casually. If you think it should be easier
to do, that's fine. I don't think *anyone* is 100% happy with the
way C is defined.

I suppose you could define a macros to make it easier:

#include <stdio.h>

#define PUN(object, type) (*(type*)&(object))

int main(void) {
unsigned u;
PUN(u, float) = 1.0/3.0;
printf("u (as float) = %g\n", PUN(u, float));
printf("u = 0x%x\n", u);
return 0;
}

[...]

Keith Thompson

unread,
Dec 5, 2012, 7:10:35 PM12/5/12
to
Ok, you're right, lvalue casts could conceivably make sense.

C doesn't have lvalue casts.

Are there any unanswered questions remaining?

glen herrmannsfeldt

unread,
Dec 5, 2012, 10:05:40 PM12/5/12
to
BartC <b...@freeuk.com> wrote:
> "James Kuyper" <james...@verizon.net> wrote in message
> news:50BF9F15...@verizon.net...
>> On 12/05/2012 01:39 PM, BartC wrote:

>>> But while A can appear on either side, (T)A can't. And there doesn't
>>> appear
>>> to be a convincing reason why not.

>> The reason is the same as the one that Keith gave to the similar
>> question you asked at the start of this thread about compound assignment
>> expressions: "The result of a cast is not an lvalue."

> OK, I get that now. But that's only because the Book says so.

(snip)

> It's not quite that either, because this might involve unwanted int/float
> conversions.

(snip)

> It means: "pretend that A is a variable of type T for this assignment".

(snip)
> Well in this case it wouldn't do anything too useful! But turning it around
> a little:

> double a;

> (int)a = 3142;

> This just writes the bit-pattern for integer 3142 in (on my machine) the
> bottom half of a. But that, I can currently do in C using instead:

OK, but now that C has complex variables:

In PL/I, you can use the functions REAL and IMAG to get the
real and imaginary parts of a complex expression, and COMPLEX to
get a complex value from two real expressions.

PL/I uses functions where C would use casts, so to get the fixed
point value from a floating point expression you use the function
FIXED.

In addition to the functions REAL, IMAG, and COMPLEX, there are also
pseudo-variables, which allow for statements like:

REAL(Z)=3;

which assigns 3 to the real part of Z, leaving the imaginary part
unchanged. You can even do:

DCL Z FIXED BIN(31,0) COMPLEX;
Z=0;
DO IMAG(Z)=1 TO 100 BY 3;
PUT SKIP LIST(Z,SQRT(Z));
END;

in which case IMAG is both a function and pseudo-variable.
(It also works with FLOAT variables.)

Other pseudo-variables are SUBSTR and UNSPEC.

-- glen

BartC

unread,
Dec 6, 2012, 7:26:14 AM12/6/12
to
"Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
news:0.8897a175ed58fc93c71b.2012...@bsb.me.uk...
> "BartC" <b...@freeuk.com> writes:

>> (byte*)p+=n;

>> In any case, the question remains, *is* there a way to cast an lvalue as
>> I've shown above?
>
> No, I don't think so. Certainly not directly -- a cast expression is
> not a lvalue. You can do dangerous thing like:
>
> *(byte **)&p += n; /* don't do this!! */
>
> or use a union with an R * and a char * pointer in it, but both
> techniques rely on the representation of char and struct pointers being
> the same -- they re-interpret the pointer rather than converting it.

I saw your comment and didn't look much further (I thought the dangers were
something to do with aliasing). Yet this seems exactly what I was looking
for.

So if someone wants an lvalue cast of the form (T)A, they can just do
*(T*)&A
instead. (Provided they know that, for example, pointer representations
happen to be compatible.)

So this statement:

"Keith Thompson" <ks...@mib.org> wrote in message
news:lnwqwwo...@nuthaus.mib.org...

> C doesn't have lvalue casts.

isn't completely true, because it seems you can get around it easily by
turning it into an rvalue cast first. (Also I'm talking about type-punning
sorts of casts rather than type-conversion ones.)

--
Bartc

Ben Bacarisse

unread,
Dec 6, 2012, 10:15:13 AM12/6/12
to
"BartC" <b...@freeuk.com> writes:

> "Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
> news:0.8897a175ed58fc93c71b.2012...@bsb.me.uk...
>> "BartC" <b...@freeuk.com> writes:
>
>>> (byte*)p+=n;
>
>>> In any case, the question remains, *is* there a way to cast an lvalue as
>>> I've shown above?
>>
>> No, I don't think so. Certainly not directly -- a cast expression is
>> not a lvalue. You can do dangerous thing like:
>>
>> *(byte **)&p += n; /* don't do this!! */
>>
>> or use a union with an R * and a char * pointer in it, but both
>> techniques rely on the representation of char and struct pointers being
>> the same -- they re-interpret the pointer rather than converting it.
>
> I saw your comment and didn't look much further (I thought the dangers were
> something to do with aliasing). Yet this seems exactly what I was looking
> for.
>
> So if someone wants an lvalue cast of the form (T)A, they can just do
> *(T*)&A instead.

The depends on the meaning you give to "lvalue cast". Since C does not
have such a thing, you have to specify it, but your original posted used
code that had different semantics. That's why, in part, I said "don't
do this" -- because it does not have the same meaning as the code you
presented.

> (Provided they know that, for example, pointer representations
> happen to be compatible.)

It seems odd to assume this when you don't need to. Your original post
complained only about the fact that your solution was rather wordy (or
messy -- I don't recall exactly), so I suggested an inline function to
tidy it up. Why would you exchange a universal solution for one with a
restriction?

> So this statement:
>
> "Keith Thompson" <ks...@mib.org> wrote in message
> news:lnwqwwo...@nuthaus.mib.org...
>
>> C doesn't have lvalue casts.
>
> isn't completely true, because it seems you can get around it easily by
> turning it into an rvalue cast first.

It's still completely true. C does not have pass by reference either,
and any technique used to get round that restriction does not alter that
fact.

> (Also I'm talking about type-punning
> sorts of casts rather than type-conversion ones.)

OK, but how could anyone have known? The code you originally posted
used entirely portable type-conversions.

--
Ben.

Willem

unread,
Dec 6, 2012, 1:35:39 PM12/6/12
to
BartC wrote:
)
)
) "Willem" <wil...@turtle.stack.nl> wrote in message
) news:slrnkbvj42...@turtle.stack.nl...
)> BartC wrote:
)> ) The offsets come from externally generated data, and initially were
)> simple
)> ) counts: +2, -1 etc. The code looked like this:
)> )
)> ) R *p,*q;
)> )
)> ) p=q+n;
)> )
)> ) Very nice. Then I noticed this addition involved internally multiplying
)> n by
)> ) 16 (or a shift as it was), which wasn't so nice! It was just as easy to
)> ) generate these numbers as multiples of 16 anyway (+32, -16 etc) so I did
)> ) that. But then the code wasn't so pretty.
)>
)> That's quite odd. A good compiler should have done that optimization for
)> you, if it's at all possible. Also, on x86 CPU's, there are addressing
)> modes that implicitly multiply by factors of two, with no speed penalty.
)
) How can it be optimised? If the pointer has a 16-byte stride, and you're
) adding an offset N, then N*16 must be added to the pointer.

I was under the impression that you were generating the offsets somewhere.
If you can pre-multiply those by 16, so can the compiler, one would think.

Take, for example:

int step = some_function();
struct struct_16_bytes_long p = start;
while (p->marker != 0) {
frobnicate(p->data);
p += step;
}

In this case, the optimizer should pull the multiply-by-16 out of the loop,
shouldn't it?


) The x86's address scaling feature only goes up to *8. This *16 is
) implemented with a shift.

I wasn't aware the stride was 16 bytes.

Keith Thompson

unread,
Dec 6, 2012, 3:05:42 PM12/6/12
to
"BartC" <b...@freeuk.com> writes:
[...]
> So this statement:
>
> "Keith Thompson" <ks...@mib.org> wrote in message
> news:lnwqwwo...@nuthaus.mib.org...
>
>> C doesn't have lvalue casts.
>
> isn't completely true, because it seems you can get around it easily by
> turning it into an rvalue cast first. (Also I'm talking about type-punning
> sorts of casts rather than type-conversion ones.)

Yes, it is completely true.

C doesn't have linked lists or binary trees either, but you can
easily implement them using structs and pointers. C gives you the
basic tools needed to build just about *anything*. Lvalue casts
(whatever you happen to mean by that phrase) are not one of those
basic tools, but they are something you can build.

And please keep in mind that there's a *big* difference between
conversion and type-punning. Conversion, as implemented by
a cast operator, converts a *value* from one type to another.
For example, a conversion from int to float gives you a float
with the mathematical value of the operand, regardless of how ints
and floats are represented. Pointer conversions conceptually do
the same thing; it just happens that most modern implementations
represent all pointers the same way, so pointer conversions can be
implemented as a reinterpretation of the representation.

Piotr Kalinowski

unread,
Dec 6, 2012, 4:00:51 PM12/6/12
to
"BartC" <b...@freeuk.com> writes:

> "Ian Collins" <ian-...@hotmail.com> wrote in message
> news:aia1eq...@mid.individual.net...
> But, I can do the equivalent of ++(char*)P by writing:
>
> P = (int*)((char*)P+1);
>
> (or near enough if that's not quite right.) So the world can still end.

It's not about disallowing you to shoot yourself. It's about making it
more difficult, so you're slightly less likely to do it. It's about
increasing the cost of ending the world, so that you'll think twice
(hopefully) before doing it.

Regards,
Piotr Kalinowski

Philipp Thomas

unread,
Dec 6, 2012, 7:24:27 PM12/6/12
to
On Wed, 05 Dec 2012 16:09:23 -0800, Keith Thompson <ks...@mib.org>
wrote:

>Or you can use a union; this avoids alignment issues, but the other
>problems still apply.

When used for type-punning that isn't guaranteed to work but depends
on the compiler.

>Or you can use memcpy() to create a *copy* of an object's representation
>in an object of a different type.

And this is the only clean way to fix type-punning and avoid alignment
issues.

Philipp

Keith Thompson

unread,
Dec 6, 2012, 8:22:47 PM12/6/12
to
Philipp Thomas <kth...@linux01.gwdg.de> writes:
> On Wed, 05 Dec 2012 16:09:23 -0800, Keith Thompson <ks...@mib.org>
> wrote:
>>Or you can use a union; this avoids alignment issues, but the other
>>problems still apply.
>
> When used for type-punning that isn't guaranteed to work but depends
> on the compiler.

A footnote in the standard (N1370 6.5.2.3p3, footnote 95) says:

If the member used to read the contents of a union object is
not the same as the member last used to store a value in the
object, the appropriate part of the object representation of
the value is reinterpreted as an object representation in the
new type as described in 6.2.6 (a process sometimes called
"type punning"). This might be a trap representation.

I'm not sure why that's stated only in a non-normative footnote.
I suppose the implication is that it's already stated normatively,
but it's not clear to me that it is.

In any case, a union does avoid alignment issues.

>>Or you can use memcpy() to create a *copy* of an object's representation
>>in an object of a different type.
>
> And this is the only clean way to fix type-punning and avoid alignment
> issues.

Philip Lantz

unread,
Dec 7, 2012, 1:49:16 AM12/7/12
to
Do you think that "B = (T)A" is similar to "memcpy(&B, &A, sizeof(T))"?

It's not.

So, if you did think that, it helps me understand why you think lvalue
casts make sense.

Öö Tiib

unread,
Dec 7, 2012, 3:31:26 AM12/7/12
to
On Friday, 7 December 2012 03:22:47 UTC+2, Keith Thompson wrote:
> Philipp Thomas <kth...@linux01.gwdg.de> writes:
> > On Wed, 05 Dec 2012 16:09:23 -0800, Keith Thompson <ks...@mib.org>
> > wrote:
> >>Or you can use a union; this avoids alignment issues, but the other
> >>problems still apply.
> >
> > When used for type-punning that isn't guaranteed to work but depends
> > on the compiler.
>
> A footnote in the standard (N1370 6.5.2.3p3, footnote 95) says:
>
> If the member used to read the contents of a union object is
> not the same as the member last used to store a value in the
> object, the appropriate part of the object representation of
> the value is reinterpreted as an object representation in the
> new type as described in 6.2.6 (a process sometimes called
> "type punning"). This might be a trap representation.
>
> I'm not sure why that's stated only in a non-normative footnote.
> I suppose the implication is that it's already stated normatively,
> but it's not clear to me that it is.

C99 has such texts:

6.2.5p20, union has an overlapping set of member objects
6.7.2.1p14, the value of at most one of the members can be stored in a union object at any time
Annex J.1 the value of a union member other than the last one stored into is unspecified.

I do not feel it safe to use union for type punning.

BartC

unread,
Dec 7, 2012, 7:54:02 AM12/7/12
to
"Philip Lantz" <p...@canterey.us> wrote in message
news:MPG.2b2a08f2d...@news.eternal-september.org...
> BartC wrote:
>>
>> "Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
>> news:0.9fd76eb692e0db06bfc4.2012...@bsb.me.uk...

>> > B = (T)A; // OK
>> > (T)A = B; // not OK

>> OK, I see it. But: the (T)A=B example might be done instead as:
>>
>> memcpy(&A, &B, sizeof(T));
>>
>> So it expresses something that could conceivably make sense. Unlike the
>> other examples that don't!
>
> Do you think that "B = (T)A" is similar to "memcpy(&B, &A, sizeof(T))"?

No. I'm interested in type-punning the left-hand-side; memcpy might be one
way of achieving that in some cases.

Your example might also work, except that (T) on the right-hand-side does
type conversion not type-punning.

> It's not.
>
> So, if you did think that, it helps me understand why you think lvalue
> casts make sense.

Since any 'lvalue cast' of the form (T)A can be written instead as *(T*)&A,
which is perfectly legal, then why shouldn't it make sense?

Look, I have this compiler project from a couple of months back. That
language also doesn't have lvalue casts (it wasn't too hot on casts, but it
*does* have the 'equivalence' feature which C doesn't have, which is what is
used instead, and is better IMO).

I decided to add lvalue casts to that language. It took ten minutes, and six
lines of code, to have them working for assignment! (Needs a bit more work
for general lvalues, and obviously a lot more testing.)

So I can now write in that language:

real x
int a

a:=x
real(a):=x # lvalue cast!

Intermediate output:

0003: 1:005 convert (a,x) int32,
real64
0004: 1:006 move (a,x)
real64, real64

And, since it was set up to produce C code, this is the final output
(obviously r64 and i32 are typedefs):

r64 x;
i32 a;

a = x;
*(r64*)&a = x;

Notice anything similar between this last line (which I haven't doctored)
and the *(T*)&A I wrote above? (I didn't even change any part of the code
generation; this is what naturally came out. However, I didn't run this
example because I just realised the destination is too small..)

Lvalue casts *can* be meaningful, and while people are right in that C
doesn't directly define them, they can be achieved with a simple
transformation.

--
Bartc

James Kuyper

unread,
Dec 7, 2012, 8:18:33 AM12/7/12
to
Keep in mind that footnote 95 merely describes what the committee
intended to be the case from the very beginning, and what is easiest to
implement, and what virtually every real implementation of C always has
implemented, because a great many C programmers have always assumed it
was true. It's not really worthwhile worrying about the possibility that
a union won't work as described by footnote 95. There's much better
things to worry about - such as the fact that type punning inherently
requires building into your code implementation-specific knowledge about
how the two types are represented.


--
James Kuyper

James Kuyper

unread,
Dec 7, 2012, 8:22:11 AM12/7/12
to
On 12/07/2012 07:54 AM, BartC wrote:
...
> Look, I have this compiler project from a couple of months back. That
> language also doesn't have lvalue casts (it wasn't too hot on casts, but it
> *does* have the 'equivalence' feature which C doesn't have, which is what is
> used instead, and is better IMO).

C does have a feature with the same semantics as your "equivalence"
feature, just different syntax. That feature is called a union.
--
James Kuyper

BartC

unread,
Dec 7, 2012, 8:48:45 AM12/7/12
to


"James Kuyper" <james...@verizon.net> wrote in message
news:k9sqi4$do2$1...@dont-email.me...
Unions are much more limited. For example, if you have:

int A[25];
double X;

you can't 'equivalence' X to A[16] without some difficulty. (Actually X
might span both A[16] and A[17].)

A might also be external, limiting the options further.

It's also harder to refer to A and X entirely independently; you might need
to use U.A and U.X (if you can even get that far).

(BTW 'equivalence' is a (now-deprecated) feature of Fortran. I thought
people might be familiar with it. My version just uses @, for example:
double X @ A[16]; )

--
Bartc

Rui Maciel

unread,
Dec 7, 2012, 10:19:33 AM12/7/12
to
BartC wrote:

> Suppose I have these types:
>
> #define byte unsigned char
>
> typedef struct {
> int a,b,c,d;
> } R; /* assume this is 16 bytes */

R stores 4 objects of type int, whose size is platform-dependent. If you
need a 4-byte integer data type then your best bet would be on int32_t,
which gives you the assurance that you will get an integer data type which
is exactly 32-bit wide.


> And these variables:
>
> int n; /* represents a *byte* offset */

If you wish to represent a byte offset then you should use size_t instead.


> R* p;
>
> I want to be able to do the following:
> p += n;
>
> but it doesn't work because n is a byte offset; it's not in terms of R
> objects.

I don't know what yo were trying to do, but if you were trying to access the
offset of a structure member by supplying the address of the struct then you
should use the offsetof() command instead of relying on pointer arithmetic
voodoo.


> But the obvious cast:
>
> (byte*)p+=n;
>
> doesn't appear to compile.

Try


> The workarounds seem to be:
>
> p += n/sizeof(R);
>
> which I don't like.

You shouldn't. No one should. It represents all kinds of badness.


> Even though p is always aligned (and n is known to be
> a multiple of 16),

How do you know that p is always aligned, and that n is a multipleof 16?
Each non-bit-field member of a struct is aligned in an implementation-
defined manner, which means that there isn't any guarantee regarding the
alignment of the structure members.

In addition, padding may or may not exist.

Finally, R was defined as a struct composed of 4 objects of type int, and
the int data type may be 8 or 16-bit wide depending on the implementation.


> and I know the divide will cancel out, it seems funny
> having to introduce a divide op in the first place. And:
>
> p = (R*)((byte*)p+n);
>
> which is what I'm using but looks very untidy in real code.
>
> In any case, the question remains, *is* there a way to cast an lvalue as
> I've shown above?

What exactly were you trying to accomplish with that code? It's rather odd
that someone tries to assign a pointer to a structure to the n-th member of
a struct of the same type.


Rui Maciel

James Kuyper

unread,
Dec 7, 2012, 11:17:55 AM12/7/12
to
On 12/07/2012 08:48 AM, BartC wrote:
>
>
> "James Kuyper" <james...@verizon.net> wrote in message
> news:k9sqi4$do2$1...@dont-email.me...
>> On 12/07/2012 07:54 AM, BartC wrote:
>> ...
>>> Look, I have this compiler project from a couple of months back. That
>>> language also doesn't have lvalue casts (it wasn't too hot on casts, but
>>> it
>>> *does* have the 'equivalence' feature which C doesn't have, which is what
>>> is
>>> used instead, and is better IMO).
>>
>> C does have a feature with the same semantics as your "equivalence"
>> feature, just different syntax. That feature is called a union.
>
> Unions are much more limited. For example, if you have:
>
> int A[25];
> double X;
>
> you can't 'equivalence' X to A[16] without some difficulty. (Actually X
> might span both A[16] and A[17].)

Your earlier description of how "equivalence" works in your language
didn't mention that it could be used in that fashion. Yes, that would be
a bit harder to do in C. If _Alignof(int) != _Alignof(double), make sure
that a[16] is correctly aligned to be used as a place to store a double
could, in principle, be problematic. The fact that you chose a power of
2 as a subscript makes this unlikely to be an issue on most machines,
but I presume your language also allows X to be equivalenced with A[17]?
If there was only one 'double' object equivalenced to A[17], you could
adjust the location of A to make sure that A[17] was correctly aligned
for a double. However, that approach wouldn't work if multiple
incompatible equivalences were specified. How does your language deal
with that? Can it only be implemented on platforms with no alignment
restrictions?

> A might also be external, limiting the options further.

In C, if A were external, code compiled to work with A could be
optimized to assume that no pointer to double ever aliases any part of
A. The union approach removes permission to make such optimizations, at
least for code within the scope of the union, but if the declaration of
A is outside your control, that's not an option.
Allowing anything in C that's similar to your language's "equivalence"
would require disabling such optimizations. Allowing it for arbitrary
types would require disabling all such optimizations; I presume that
such optimizations are prohibited in your language? Or perhaps code
which would interact badly with such optimizations is prohibited? That
would be the equivalent of restrict-qualifying most pointer declarations
in C.

> It's also harder to refer to A and X entirely independently; you might need
> to use U.A and U.X (if you can even get that far).
>
> (BTW 'equivalence' is a (now-deprecated) feature of Fortran. I thought
> people might be familiar with it. My version just uses @, for example:
> double X @ A[16]; )

Its been a couple of decades since the last time I wrote much Fortran
code, and I don't think I ever used that feature, but I was aware of it.
If it has the same capability you describe above, I didn't remember that
fact, which isn't particularly surprising.

BartC

unread,
Dec 7, 2012, 11:54:29 AM12/7/12
to
"Rui Maciel" <rui.m...@gmail.com> wrote in message
news:k9t1e6$o0i$1...@dont-email.me...
> BartC wrote:

>> Even though p is always aligned (and n is known to be
>> a multiple of 16),
>
> How do you know that p is always aligned

p points into a malloc-allocated array of such structs.

>> , and that n is a multipleof 16?

Because all such values (originally +2, -1 etc) have been multiplied by 16
(or rather, sizeof(R)) in an initial pass of the data!

>> p = (R*)((byte*)p+n);

> What exactly were you trying to accomplish with that code? It's rather
> odd
> that someone tries to assign a pointer to a structure to the n-th member
> of
> a struct of the same type.

p points into an array of structs. It's modified to point instead at a +2
or -1 etc offset from where it currently is.

But I decided to change that +2, -1 etc to +32, -16 etc because I didn't
like the 80 million extra left-shifts being executed every second for no
purpose.

--
Bartc

Öö Tiib

unread,
Dec 7, 2012, 12:07:25 PM12/7/12
to
Yes, I am not worried how union is implemented, I am worried if an
optimizer might ignore that footnote and optimize too lot in some
case. So ... I prefer to memcpy or to cast (properly aligned) pointers.

glen herrmannsfeldt

unread,
Dec 7, 2012, 12:33:29 PM12/7/12
to
James Kuyper <james...@verizon.net> wrote:
> On 12/07/2012 08:48 AM, BartC wrote:

(snip, someone wrote)
>>> C does have a feature with the same semantics as your "equivalence"
>>> feature, just different syntax. That feature is called a union.

>> Unions are much more limited. For example, if you have:

>> int A[25];
>> double X;

>> you can't 'equivalence' X to A[16] without some difficulty. (Actually X
>> might span both A[16] and A[17].)

> Your earlier description of how "equivalence" works in your language
> didn't mention that it could be used in that fashion. Yes, that would be
> a bit harder to do in C. If _Alignof(int) != _Alignof(double), make sure
> that a[16] is correctly aligned to be used as a place to store a double
> could, in principle, be problematic.

(snip)

>> It's also harder to refer to A and X entirely independently;
>> you might need to use U.A and U.X (if you can even get that far).

>> (BTW 'equivalence' is a (now-deprecated) feature of Fortran.
>> I thought people might be familiar with it. My version just
>> uses @, for example: double X @ A[16]; )

> Its been a couple of decades since the last time I wrote much Fortran
> code, and I don't think I ever used that feature, but I was aware of it.
> If it has the same capability you describe above, I didn't remember that
> fact, which isn't particularly surprising.

Fortran EQUIVALENCE has many of the same restrictions as union, though
it is often used to get bits between different types.

Fortran now has TRANSFER, an intrinsic function that is defined
to "transfer the physical representation." It also avoids any
questions about alignment.

But then C has memcpy() and casts to (unsigned char *) to do it.

-- glen

BartC

unread,
Dec 7, 2012, 1:27:04 PM12/7/12
to
"James Kuyper" <james...@verizon.net> wrote in message
news:50C216B...@verizon.net...
> On 12/07/2012 08:48 AM, BartC wrote:

>> Unions are much more limited. For example, if you have:
>>
>> int A[25];
>> double X;
>>
>> you can't 'equivalence' X to A[16] without some difficulty. (Actually X
>> might span both A[16] and A[17].)

> but I presume your language also allows X to be equivalenced with A[17]?

Yes. Or one byte past. (Well, if the hardware allows it, why not?)

> However, that approach wouldn't work if multiple
> incompatible equivalences were specified. How does your language deal
> with that?

The language is dumb. If you override the default alignments by using this
aliasing, then it will use exactly the address you gave it. Just like you
can do in assembly code.

> Can it only be implemented on platforms with no alignment
> restrictions?

Usually the feature will be used sensibly. Doubly so if unaligned accesses
cause a crash. Although it's possible to deal with that properly; I've seen
gcc generate byte-at-a-time code for accessing an int via a pointer it
thought was unaligned.

> I presume that
> such optimizations are prohibited in your language?

Not in the language. But my compilers never did sophisticated optimisations
anyway. (Actually I don't think they did unsophisticated ones either...)

> Or perhaps code
> which would interact badly with such optimizations is prohibited?

I don't think optimisations would need to be entirely ruled out. All the
information is there at compile-time (unlike pointers where you're never
quite sure what's pointing to what.) And you wouldn't use this stuff
everywhere.

--
Bartc

BartC

unread,
Dec 7, 2012, 1:28:06 PM12/7/12
to
"glen herrmannsfeldt" <g...@ugcs.caltech.edu> wrote in message
news:k9t999$r5g$1...@speranza.aioe.org...
> James Kuyper <james...@verizon.net> wrote:
>> On 12/07/2012 08:48 AM, BartC wrote:


>>> (BTW 'equivalence' is a (now-deprecated) feature of Fortran.
>>> I thought people might be familiar with it.

>> Its been a couple of decades since the last time I wrote much Fortran
>> code, and I don't think I ever used that feature, but I was aware of it.
>> If it has the same capability you describe above, I didn't remember that
>> fact, which isn't particularly surprising.
>
> Fortran EQUIVALENCE has many of the same restrictions as union, though
> it is often used to get bits between different types.

This page shows you can still set up all sorts of relationships between
unrelated data:

http://docs.oracle.com/cd/E19957-01/805-4939/6j4m0vn9b/index.html

--
Bartc

Keith Thompson

unread,
Dec 7, 2012, 2:01:33 PM12/7/12
to
Note that C99 doesn't have that footnote, but N1256 does. It was added
by one of Technical Corrigenda 3 -- but N1256 (which incorporates TC3)
still has that clause in Annex J.1. I suspect this is just an
oversight.

N1570 (essentially C2011) changed the wording of the clause in J.1
(which is a list of unspecified behaviors) from:
The value of a union member other than the last one stored into
(6.2.6.1)
to:
The values of bytes that correspond to union members other than the
one last stored into (6.2.6.1).

I can't find the list of C99 DRs (the link I had,
<http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary.htm>
now points to the list of C2011 DRs), so I don't know which DR
introduced this change.

But the implication is that the committee felt that the existing C99
standard already implied what the footnote says. It's conceivable
that a compiler could behave differently, (e.g., by optimizing away
an assignment to a union member if that member is never access again)
-- but such a compiler would break a good deal of existing code that
depends on this behavior, whether it's guaranteed by the standard
or not.

James Kuyper

unread,
Dec 7, 2012, 2:14:33 PM12/7/12
to
On 12/07/2012 01:27 PM, BartC wrote:
> "James Kuyper" <james...@verizon.net> wrote in message
> news:50C216B...@verizon.net...
...
>> I presume that
>> such optimizations are prohibited in your language?
>
> Not in the language. But my compilers never did sophisticated optimisations
> anyway. (Actually I don't think they did unsophisticated ones either...)
>
>> Or perhaps code
>> which would interact badly with such optimizations is prohibited?
>
> I don't think optimisations would need to be entirely ruled out. All the
> information is there at compile-time (unlike pointers where you're never
> quite sure what's pointing to what.) And you wouldn't use this stuff
> everywhere.

I'm not talking about all optimizations, just the ones that depend upon
C's anti-aliasing guarantees. Consider the following function:

void func(int n, float array[static n], long *l)
{
for(int i=0; i<n; i++)
array[i] = *l;
*l = n;
}

This isn't a particularly useful function - I've chosen it solely to
clearly illustrate my point - but my point also applies to more
complicated and useful functions.
An implementation is permitted, because of 6.5p7, to compile that
function as if it had been written:

void func(int n, float array[static n], long *l)
{
long temp = *l;
for(int i=0; i<n; i++)
array[i] = temp;
*l = n;
}

The optimized version has exactly the same behavior as the original, so
long as 6.5p7 is not violated. If C were changed to allow a long integer
to be equivalenced with a element of a float array, the value of *l
might change during the loop. If so, these two versions of the code
would no longer be equivalent, and there's an important question to be
asked about which one actually matches the user's intent. If the initial
value of *l was carefully chosen with the intent that it would, in fact,
be changed by this code inside that loop, the developer is going to be a
little upset if that didn't actually happen.

BartC

unread,
Dec 7, 2012, 3:16:26 PM12/7/12
to
"James Kuyper" <james...@verizon.net> wrote in message
news:50C24019...@verizon.net...
> On 12/07/2012 01:27 PM, BartC wrote:

>> I don't think optimisations would need to be entirely ruled out. All the
>> information is there at compile-time (unlike pointers where you're never
>> quite sure what's pointing to what.)

> I'm not talking about all optimizations, just the ones that depend upon
> C's anti-aliasing guarantees. Consider the following function:
>
> void func(int n, float array[static n], long *l)
> {
> for(int i=0; i<n; i++)
> array[i] = *l;
> *l = n;
> }

> An implementation is permitted, because of 6.5p7, to compile that
> function as if it had been written:

> void func(int n, float array[static n], long *l)
> {
> long temp = *l;
> for(int i=0; i<n; i++)
> array[i] = temp;
> *l = n;
> }
>
> The optimized version has exactly the same behavior as the original, so
> long as 6.5p7 is not violated. If C were changed to allow a long integer
> to be equivalenced with a element of a float array, the value of *l
> might change during the loop.

(Seems like it might just be copied to itself? In this example).

>If so, these two versions of the code
> would no longer be equivalent, and there's an important question to be
> asked about which one actually matches the user's intent. If the initial
> value of *l was carefully chosen with the intent that it would, in fact,
> be changed by this code inside that loop, the developer is going to be a
> little upset if that didn't actually happen.

I can't see that the problems are going to be that different from the ones
you might get when using pointers. (And the solutions might be similar.)

Because even without equivalencing, 'l' might anyway point into the array,
via a cast.

If this was part of the language, at least the aliasing would be done in an
overt manner, not hidden away in a pointer cast, in an operation done at
runtime so you don't know at any time what is aliasing what.

--
bartc

James Kuyper

unread,
Dec 7, 2012, 3:41:34 PM12/7/12
to
On 12/07/2012 03:16 PM, BartC wrote:
> "James Kuyper" <james...@verizon.net> wrote in message
> news:50C24019...@verizon.net...
...
>> I'm not talking about all optimizations, just the ones that depend upon
>> C's anti-aliasing guarantees. Consider the following function:
>>
>> void func(int n, float array[static n], long *l)
>> {
>> for(int i=0; i<n; i++)
>> array[i] = *l;
>> *l = n;
>> }
>
>> An implementation is permitted, because of 6.5p7, to compile that
>> function as if it had been written:
>
>> void func(int n, float array[static n], long *l)
>> {
>> long temp = *l;
>> for(int i=0; i<n; i++)
>> array[i] = temp;
>> *l = n;
>> }
>>
>> The optimized version has exactly the same behavior as the original, so
>> long as 6.5p7 is not violated. If C were changed to allow a long integer
>> to be equivalenced with a element of a float array, the value of *l
>> might change during the loop.
>
> (Seems like it might just be copied to itself? In this example).

I think you're assuming that sizeof(long)==sizeof(float), which is
commonplace, but not required. However, even on systems where that is
true, the result is not a simple copy. The conversion to float will
produce a bit pattern that's pretty unlikely to be the same as the
original (unless the original represents 0).

> >If so, these two versions of the code
>> would no longer be equivalent, and there's an important question to be
>> asked about which one actually matches the user's intent. If the initial
>> value of *l was carefully chosen with the intent that it would, in fact,
>> be changed by this code inside that loop, the developer is going to be a
>> little upset if that didn't actually happen.
>
> I can't see that the problems are going to be that different from the ones
> you might get when using pointers. (And the solutions might be similar.)

In C, such code using pointer casts violates 6.5p7, and therefore has
undefined behavior, so the solution is simply not to write such code.

> Because even without equivalencing, 'l' might anyway point into the array,
> via a cast.

Not with defined behavior.

> If this was part of the language, at least the aliasing would be done in an
> overt manner, not hidden away in a pointer cast, in an operation done at
> runtime so you don't know at any time what is aliasing what.

As I said, the C language does provide a way to explicitly alias objects
of two different types, and the optimizations I've described above are
therefore prohibited when they occur within the scope of a union
declaration that connects the relevant types (except in those contexts
where the compiler can be certain that the relevant pointers do not
point at members of the same union object).

glen herrmannsfeldt

unread,
Dec 7, 2012, 4:57:02 PM12/7/12
to
Keith Thompson <ks...@mib.org> wrote:

(snip, someone wrote)
>> I do not feel it safe to use union for type punning.

> Note that C99 doesn't have that footnote, but N1256 does. It was added
> by one of Technical Corrigenda 3 -- but N1256 (which incorporates TC3)
> still has that clause in Annex J.1. I suspect this is just an
> oversight.

(snip)

> But the implication is that the committee felt that the existing C99
> standard already implied what the footnote says. It's conceivable
> that a compiler could behave differently, (e.g., by optimizing away
> an assignment to a union member if that member is never access again)
> -- but such a compiler would break a good deal of existing code that
> depends on this behavior, whether it's guaranteed by the standard
> or not.

Some time ago, I was trying to figure out if it would be
possible for a C compiler to generate JVM code.

JVM has no operation that stores different types in the same memory.
I believe it isn't so hard to make a memcpy() that can do the
appropriate conversions and copies, though.

It seemed to me at the time that implementing union the same as
struct was one way that would work, and would also follow the
standard. (Though maybe less memory efficient.)

I asked here at the time, and many seemed to agree, though as you
say, it might break existing code.

-- glen

glen herrmannsfeldt

unread,
Dec 7, 2012, 5:18:05 PM12/7/12
to
BartC <b...@freeuk.com> wrote:
> "glen herrmannsfeldt" <g...@ugcs.caltech.edu> wrote in message
> news:k9t999$r5g$1...@speranza.aioe.org...
>> James Kuyper <james...@verizon.net> wrote:

(snip on EQUIVALENCE)

>>> Its been a couple of decades since the last time I wrote much Fortran
>>> code, and I don't think I ever used that feature, but I was aware of it.
>>> If it has the same capability you describe above, I didn't remember
>>> that fact, which isn't particularly surprising.

>> Fortran EQUIVALENCE has many of the same restrictions as union, though
>> it is often used to get bits between different types.

> This page shows you can still set up all sorts of relationships between
> unrelated data:

> http://docs.oracle.com/cd/E19957-01/805-4939/6j4m0vn9b/index.html

EQUIVALENCE goes back to the first Fortran compiler, usually called
Fortran I, in 1956. It required at least 4K (36 bit) words to run.
A 704 with the full 32K (36 bit) words was pretty much the super
computer of the time. EQUIVALENCE was needed to conserve memory
use, and much less for type punning.

But yes, in later years, with more memory available, EQUIVALENCE
was commonly used to move bits between different data types.

TRANSFER wasn't added until, I believe, Fortran 90.
(Fortran 90 added dynamic allocation, which doesn't work
with EQUIVALENCE.)

-- glen

Ben Bacarisse

unread,
Dec 7, 2012, 5:29:41 PM12/7/12
to
I don't think that would work out. In particular, 6.5.8 p5 says "All
pointers to members of the same union object compare equal". Since you
are talking about a compiler here, that alone is not the end of the
matter because the compiler might be able to arrange for this to appear
to be true. For example, a pointer might be made to consist of two
parts, one that is used for comparison while the other part is an offset
used for union access, but I fear that this won't fly in the long run.
(For example, this particular ruse will go wrong implementing the
special provision for "common initial sequences" in 6.5.2.3 p5. Again,
a fix-up is possible but I am sceptical about far this can go on.)

It would be an interesting exercise to see if would work, but I'm not
hopeful.

--
Ben.

glen herrmannsfeldt

unread,
Dec 7, 2012, 7:04:32 PM12/7/12
to
Ben Bacarisse <ben.u...@bsb.me.uk> wrote:

(snip, I wrote)
>> Some time ago, I was trying to figure out if it would be
>> possible for a C compiler to generate JVM code.

>> JVM has no operation that stores different types in the same memory.
>> I believe it isn't so hard to make a memcpy() that can do the
>> appropriate conversions and copies, though.

>> It seemed to me at the time that implementing union the same as
>> struct was one way that would work, and would also follow the
>> standard. (Though maybe less memory efficient.)

>> I asked here at the time, and many seemed to agree, though as you
>> say, it might break existing code.

> I don't think that would work out. In particular, 6.5.8 p5 says "All
> pointers to members of the same union object compare equal". Since you
> are talking about a compiler here, that alone is not the end of the
> matter because the compiler might be able to arrange for this to appear
> to be true. For example, a pointer might be made to consist of two
> parts, one that is used for comparison while the other part is an offset
> used for union access,

Well, most pointers have to have an Object reference to an array
and an offset into the array. Any scalar that can be pointed to
has to instead be an array of length 1.

> but I fear that this won't fly in the long run.
> (For example, this particular ruse will go wrong implementing the
> special provision for "common initial sequences" in 6.5.2.3 p5. Again,
> a fix-up is possible but I am sceptical about far this can go on.)

As well as I know, all that fun stuff only has to happen for
(unsigned char *), or (void *), so the compiler only has to do that
extra stuff when those occur.

But yes, getting that one right will be tricky.

> It would be an interesting exercise to see if would work, but I'm not
> hopeful.

It already gets tricky for struct. If you make a struct a Java class,
then you can have an object reference to the (instance of the) class,
which is different from a reference to a class member. So, extra
work to get that right.

-- glen

Phil Carmody

unread,
Dec 9, 2012, 10:54:05 AM12/9/12
to
"BartC" <b...@freeuk.com> writes:
> "Eric Sosman" <eso...@comcast-dot-net.invalid> wrote:
...
> >> I asked about doing the same on the left side of an assignment.
> >>
> >> It's this asymmetry that is the issue.
> >
> > Um, er, assignment itself is asymmetric ...
>
> Plenty of terms can appear interchangeably on both left and right sides.
>
> But while A can appear on either side, (T)A can't. And there doesn't
> appear to be a convincing reason why not.

Your posts are getting more and more detached from reality. What would

(T)A = expr;

even mean? What is getting modified? Certainly A can't be. One could argue
that some temporary non-addressible pseudo-object containing the cast value
of A is being modified, but as that pseudo-object fails to be referenceable
after that line, the assignment is absolutely useless.

I want neither meaninglessness nor uselessness as a language feature.

Phil
--
I'm not saying that google groups censors my posts, but there's a strong link
between me saying "google groups sucks" in articles, and them disappearing.

Oh - I guess I might be saying that google groups censors my posts.

James Kuyper

unread,
Dec 9, 2012, 12:38:55 PM12/9/12
to
On 12/09/2012 10:54 AM, Phil Carmody wrote:
> "BartC" <b...@freeuk.com> writes:
>> "Eric Sosman" <eso...@comcast-dot-net.invalid> wrote:
> ...
>>>> I asked about doing the same on the left side of an assignment.
>>>>
>>>> It's this asymmetry that is the issue.
>>>
>>> Um, er, assignment itself is asymmetric ...
>>
>> Plenty of terms can appear interchangeably on both left and right sides.
>>
>> But while A can appear on either side, (T)A can't. And there doesn't
>> appear to be a convincing reason why not.
>
> Your posts are getting more and more detached from reality. What would
>
> (T)A = expr;
>
> even mean? What is getting modified? Certainly A can't be. One could argue
> that some temporary non-addressible pseudo-object containing the cast value
> of A is being modified, but as that pseudo-object fails to be referenceable
> after that line, the assignment is absolutely useless.
>
> I want neither meaninglessness nor uselessness as a language feature.

He's made it clear what he wants it to mean. What he wants is equivalent
to the following C code, except for the fact that the following code
violates a constraint, and he wants this construct to have well-defined
behavior:

*(T*)&A = expr;

That this construct would have such a meaning is inconsistent with the
way casts work in the rest of the C language, which doesn't bother him;
in fact, I think he believes it's inconsistent for it to NOT work this
way. It would still be a horrible unsafe thing to do, even if it were
fully legal. It can be done somewhat more safely by declaring a union,
but he wants to be able to use it even in contexts where he can't change
the definition of A to be a union, and the syntax for doing it using a
union is more complicated than he wants it to be. Also, he thinks the
syntax to do this using a union is excessively complicated.
--
James Kuyper

BartC

unread,
Dec 9, 2012, 1:50:30 PM12/9/12
to
"James Kuyper" <james...@verizon.net> wrote in message
news:ka2ibh$kau$1...@dont-email.me...
> On 12/09/2012 10:54 AM, Phil Carmody wrote:

>> Your posts are getting more and more detached from reality. What would
>>
>> (T)A = expr;

>> I want neither meaninglessness nor uselessness as a language feature.

(Neither do I.)

> He's made it clear what he wants it to mean.

> *(T*)&A = expr;

> It can be done somewhat more safely by declaring a union,
> but he wants to be able to use it even in contexts where he can't change
> the definition of A to be a union, and the syntax for doing it using a
> union is more complicated than he wants it to be. Also, he thinks the
> syntax to do this using a union is excessively complicated.

It's not that complicated, but it's intrusive.

It would also affect uses of 'A' throughout the project. (And perhaps a
different module might want to apply a different union!) Sometimes you might
just want to use such a cast locally. Or it might be experimental, and you
want to try out something without turning the whole project upside-down.

There are plenty of uses for the feature. And if a union-like behaviour is
the way to do it, perhaps the compiler can take care of it, in trivial
cases. Or the rules for unions can be relaxed. So instead of having to
change;

S* p;

to:

union {
S* s;
T* t;
} p;

and changing every p to p.s or p.t, simpler to allow an anonymous union, for
example:

union {
S* p;
};

Now p can be used as before, but the compiler knows there might be aliasing
issues. This still requires the cast. Or:

union {
S* p;
T* pt;
};

Now p is only changed in a few places to pt, instead of using a cast. (As a
bonus, it would be nice to augment an existing union too.)

--
Bartc



James Kuyper

unread,
Dec 9, 2012, 2:31:53 PM12/9/12
to
On 12/09/2012 01:50 PM, BartC wrote:
...
> There are plenty of uses for the feature. And if a union-like behaviour is
> the way to do it, perhaps the compiler can take care of it, in trivial
> cases. Or the rules for unions can be relaxed. So instead of having to
> change;
>
> S* p;
>
> to:
>
> union {
> S* s;
> T* t;
> } p;
>
> and changing every p to p.s or p.t, simpler to allow an anonymous union, for
> example:
>
> union {
> S* p;
> };

That wouldn't do anything useful. It doesn't tell the compiler which
other types p is aliased with, so it can't know how much space to
reserve for the union, nor what alignment requirements the union will have.

> Now p can be used as before, but the compiler knows there might be aliasing
> issues. This still requires the cast. Or:
>
> union {
> S* p;
> T* pt;
> };

That, on the other hand, would work, and is trivially compatible with
the current C language. Anonymous unions are already allowed as members
in structures (as of C2011), and stand-alone anonymous unions have, I
believe, long been a common extension to C.
--
James Kuyper

Keith Thompson

unread,
Dec 9, 2012, 11:12:57 PM12/9/12
to
James Kuyper <james...@verizon.net> writes:
> On 12/09/2012 01:50 PM, BartC wrote:
[...]
>> union {
>> S* p;
>> T* pt;
>> };
>
> That, on the other hand, would work, and is trivially compatible with
> the current C language. Anonymous unions are already allowed as members
> in structures (as of C2011), and stand-alone anonymous unions have, I
> believe, long been a common extension to C.

I think you may be mistaken on that last point.

Are you saying that (with such an extension), this:

union {
unsigned u;
float f;
};
f = 1.0;
printf("0x%x\n", u);

would be allowed? I haven't seen that; gcc warns "unnamed
struct/union that defines no instances". In other words, a
standalone anonymous union is already valid but useless.

Heikki Kallasjoki

unread,
Dec 10, 2012, 2:38:36 AM12/10/12
to
On 2012-12-10, Keith Thompson <ks...@mib.org> wrote:
> James Kuyper <james...@verizon.net> writes:
>> the current C language. Anonymous unions are already allowed as members
>> in structures (as of C2011), and stand-alone anonymous unions have, I
>> believe, long been a common extension to C.
>
> I think you may be mistaken on that last point.
>
> Are you saying that (with such an extension), this:
>
> union {
> unsigned u;
> float f;
> };
> f = 1.0;
> printf("0x%x\n", u);
>
> would be allowed? I haven't seen that; gcc warns "unnamed
> struct/union that defines no instances". In other words, a
> standalone anonymous union is already valid but useless.

At least from C99 onwards, I don't think it's "already valid"; based on
the constraint in C99 6.7p2:

A declaration shall declare at least a declarator (other than the
parameters of a function or the members of a structure or union), a
tag, or the members of an enumeration.

It would therefore be a non-conflicting extension to assign some
compiler-specific meaning on such code. (Though I have not come across
such an extension anywhere either.)

--
Heikki Kallasjoki

Ian Collins

unread,
Dec 10, 2012, 2:50:15 AM12/10/12
to
Keith Thompson wrote:
> James Kuyper <james...@verizon.net> writes:
>> On 12/09/2012 01:50 PM, BartC wrote:
> [...]
>>> union {
>>> S* p;
>>> T* pt;
>>> };
>>
>> That, on the other hand, would work, and is trivially compatible with
>> the current C language. Anonymous unions are already allowed as members
>> in structures (as of C2011), and stand-alone anonymous unions have, I
>> believe, long been a common extension to C.
>
> I think you may be mistaken on that last point.
>
> Are you saying that (with such an extension), this:
>
> union {
> unsigned u;
> float f;
> };
> f = 1.0;
> printf("0x%x\n", u);
>
> would be allowed? I haven't seen that; gcc warns "unnamed
> struct/union that defines no instances". In other words, a
> standalone anonymous union is already valid but useless.

Sun cc is a little less subtle:
warning: useless declaration

What did surprise me is the code appears to be valid C++! I guess you
learn something new every day.

--
Ian Collins

James Kuyper

unread,
Dec 10, 2012, 7:49:52 AM12/10/12
to
On 12/09/2012 11:12 PM, Keith Thompson wrote:
> James Kuyper <james...@verizon.net> writes:
>> On 12/09/2012 01:50 PM, BartC wrote:
> [...]
>>> union {
>>> S* p;
>>> T* pt;
>>> };
>>
>> That, on the other hand, would work, and is trivially compatible with
>> the current C language. Anonymous unions are already allowed as members
>> in structures (as of C2011), and stand-alone anonymous unions have, I
>> believe, long been a common extension to C.
>
> I think you may be mistaken on that last point.
>
> Are you saying that (with such an extension), this:
>
> union {
> unsigned u;
> float f;
> };
> f = 1.0;
> printf("0x%x\n", u);
>
> would be allowed? I haven't seen that; gcc warns "unnamed
> struct/union that defines no instances". In other words, a
> standalone anonymous union is already valid but useless.

They are permitted and useful in C++; that may be what I was thinking of.
--
James Kuyper

James Kuyper

unread,
Dec 10, 2012, 8:12:31 AM12/10/12
to
As I said, it's "already allowed as members in structures (as of
C2011)", not C99. See 6.7.2.1p13: "an unnamed member whose type
specifier is a union specifier with no tag is called an anonymous
union.", where "anonymous union" is italicized, indicating that this
sentence serves to define that term.

However, they don't seem to have made a corresponding change to 6.7p2. I
don't see any way to interpret it that's compatible with the anonymous
structure and union members that were added in C2011. That may have been
an oversight - hopefully Larry Jones can comment?
--
James Kuyper

Heikki Kallasjoki

unread,
Dec 10, 2012, 12:46:42 PM12/10/12
to
On 2012-12-10, James Kuyper <james...@verizon.net> wrote:
> On 12/10/2012 02:38 AM, Heikki Kallasjoki wrote:
>> On 2012-12-10, Keith Thompson <ks...@mib.org> wrote:
>>> James Kuyper <james...@verizon.net> writes:
>>>> the current C language. Anonymous unions are already allowed as members
>>>> in structures (as of C2011), and stand-alone anonymous unions have, I
>>>> believe, long been a common extension to C.
>>>
>>> I think you may be mistaken on that last point.
>>>
>>> Are you saying that (with such an extension), this:
>>>
>>> union {
>>> unsigned u;
>>> float f;
>>> };
>>> f = 1.0;
>>> printf("0x%x\n", u);
>>>
>>> would be allowed? I haven't seen that; gcc warns "unnamed
>>> struct/union that defines no instances". In other words, a
>>> standalone anonymous union is already valid but useless.
^^^^^^^^^^^^^
>> At least from C99 onwards, I don't think it's "already valid"; based on
>> the constraint in C99 6.7p2:
[..]
>
> As I said, it's "already allowed as members in structures (as of
> C2011)", not C99. See 6.7.2.1p13: "an unnamed member whose type

It was, however, the highlighted instance of "already valid" from Keith
Thompson that I was commenting on; the one considering the standalone
case.

(Apologies for the confusion.)

--
Heikki Kallasjoki

James Kuyper

unread,
Dec 10, 2012, 12:59:37 PM12/10/12
to
It was my confusion - your wording was clear enough. I'm not sure how I
managed to misread it that way. I remember looking for, and not finding,
the word 'valid' in the preceding text, so I must not have been fully
awake when I performed that search. Therefore, I keyed in on the only
use of 'already' that I could find (another indication that I wasn't
fully awake).

Keith Thompson

unread,
Dec 10, 2012, 3:07:15 PM12/10/12
to
Your statement was both sufficiently clear and correct; a standalone
anonymous union is *not* valid in C99 (i.e., I was mistaken).

lawrenc...@siemens.com

unread,
Dec 15, 2012, 4:51:06 PM12/15/12
to
Keith Thompson <ks...@mib.org> wrote:
>
> I can't find the list of C99 DRs (the link I had,
> <http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary.htm>
> now points to the list of C2011 DRs), so I don't know which DR
> introduced this change.

C89 DRs: <http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr.htm>

C99 DRs: <http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary-c99.htm>
--
Larry Jones

What this games needs are negotiated settlements. -- Calvin

Keith Thompson

unread,
Dec 15, 2012, 6:48:39 PM12/15/12
to
lawrenc...@siemens.com writes:
> Keith Thompson <ks...@mib.org> wrote:
>>
>> I can't find the list of C99 DRs (the link I had,
>> <http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary.htm>
>> now points to the list of C2011 DRs), so I don't know which DR
>> introduced this change.
>
> C89 DRs: <http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr.htm>
>
> C99 DRs: <http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary-c99.htm>

Thanks!

(Small annoying quibble: Those are C90 DRs, not C89 DRs; they refer to
the C90 section numbers.)

Tim Rentsch

unread,
Dec 17, 2012, 5:43:11 AM12/17/12
to
Keith Thompson <ks...@mib.org> writes:

> Philipp Thomas <kth...@linux01.gwdg.de> writes:
>> On Wed, 05 Dec 2012 16:09:23 -0800, Keith Thompson <ks...@mib.org>
>> wrote:
>>>Or you can use a union; this avoids alignment issues, but the other
>>>problems still apply.
>>
>> When used for type-punning that isn't guaranteed to work but depends
>> on the compiler.
>
> A footnote in the standard (N1370 6.5.2.3p3, footnote 95) says:
>
> If the member used to read the contents of a union object is
> not the same as the member last used to store a value in the
> object, the appropriate part of the object representation of
> the value is reinterpreted as an object representation in the
> new type as described in 6.2.6 (a process sometimes called
> "type punning"). This might be a trap representation.
>
> I'm not sure why that's stated only in a non-normative footnote.
> I suppose the implication is that it's already stated normatively,
> but it's not clear to me that it is.

I've explained this before. It's a consequence of the definition
of union types and the normal rules for lvalue conversion. There
isn't anything hard about it for anyone willing to sit down and
trace through the different sections; all it takes is checking
that everything is defined and what the defined semantics are.
Basically, if variable access "works", then doing a type-punning
access on a union member has to "work", because the same semantics
are invoked in the two cases.

Tim Rentsch

unread,
Dec 17, 2012, 5:53:09 AM12/17/12
to
[someone] writes:

> On Friday, 7 December 2012 03:22:47 UTC+2, Keith Thompson wrote:
>> Philipp Thomas <kth...@linux01.gwdg.de> writes:
>> > On Wed, 05 Dec 2012 16:09:23 -0800, Keith Thompson <ks...@mib.org>
>> > wrote:
>> >>Or you can use a union; this avoids alignment issues, but the other
>> >>problems still apply.
>> >
>> > When used for type-punning that isn't guaranteed to work but depends
>> > on the compiler.
>>
>> A footnote in the standard (N1370 6.5.2.3p3, footnote 95) says:
>>
>> If the member used to read the contents of a union object is
>> not the same as the member last used to store a value in the
>> object, the appropriate part of the object representation of
>> the value is reinterpreted as an object representation in the
>> new type as described in 6.2.6 (a process sometimes called
>> "type punning"). This might be a trap representation.
>>
>> I'm not sure why that's stated only in a non-normative footnote.
>> I suppose the implication is that it's already stated normatively,
>> but it's not clear to me that it is.
>
> C99 has such texts:
>
> 6.2.5p20, union has an overlapping set of member objects
> 6.7.2.1p14, the value of at most one of the members can be stored in a union object at any time
> Annex J.1 the value of a union member other than the last one stored into is unspecified.
>
> I do not feel it safe to use union for type punning.

And you feel that way despite the Standard clearly stating
in plain English that this is how union access /does/ work,
including explaining that it is called 'type punning'?

Tim Rentsch

unread,
Dec 17, 2012, 5:58:36 AM12/17/12
to
Keith Thompson <ks...@mib.org> writes:
> standard already implied what the footnote says. [snip]

In fact, the footnote was added precisely because someone was
concerned it wasn't obvious (or obvious enough) that C99's
rules produced the same behavior (ie, type punning) for union
access as had been true before C99.

Tim Rentsch

unread,
Dec 17, 2012, 6:03:00 AM12/17/12
to
> declaration that connects the relevant types [snip unimportant exception].

I think you'd get some disagreement on this last point. It
is not enough that the types in question just co-reside in a
visible union type. If that were true there would be no reason
for the "special rule" about struct's and accessing their
common prefix.

Tim Rentsch

unread,
Dec 17, 2012, 6:11:35 AM12/17/12
to
6.7p2 applies to declarations. The elements defining the contents
of a struct (or union) type, including anonymous structs (or unions)
are struct-declarations, not declarations. 6.7p2 doesn't apply.

lawrenc...@siemens.com

unread,
Dec 17, 2012, 4:37:38 PM12/17/12
to
Keith Thompson <ks...@mib.org> wrote:
> lawrenc...@siemens.com writes:
> > Keith Thompson <ks...@mib.org> wrote:
> >>
> >> I can't find the list of C99 DRs (the link I had,
> >> <http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary.htm>
> >> now points to the list of C2011 DRs), so I don't know which DR
> >> introduced this change.
> >
> > C89 DRs: <http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr.htm>
> >
> > C99 DRs: <http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary-c99.htm>
>
> Thanks!
>
> (Small annoying quibble: Those are C90 DRs, not C89 DRs; they refer to
> the C90 section numbers.)

Oops, right you are! DRs are an ISO thing, so they naturally refer to
the ISO standard rather than the ANSI one.
--
Larry Jones

It seems like once people grow up, they have no idea what's cool. -- Calvin
0 new messages