Neatest way to get the end pointer?

  • Thread starter Tomás Ó hÉilidhe
  • Start date
H

Harald van Dijk

The declaration was

    int my_array[X];
[...]
*(&my_array + 1)

I thought this was a case where the last sentence of 6.5.6 p8 applies:
"If the result points one past the last element of the array object, it
shall not be used as the operand of a unary * operator that is
evaluated.". The "is evaluated" seems, to me, to reserved for the &*E
case where the validity of E is not significant. Surely the * "is
evaluated" in this case even though a decay to a pointer is the
immediate result?

I believe you're right. The only special case where unary * is not
considered evaluated is when it's the immediate operand of unary &. While
the array-to-pointer conversion does take the array's address, there is
no operator used to do this, so the special case doesn't apply.
 
T

Tomás Ó hÉilidhe

Chris Thomasson:
____________________________________________________________
#include <stdio.h>

typedef char static_assert[
sizeof(long int) == sizeof(int) ? 1 : -1
];

typedef union foo_u {
long int a;
int b;
} foo;

int main(void) {
foo f = { 0 };
f.a = 1;
printf("%d\n", f.b);
return 0;
}


____________________________________________________________



You might want another static_assert to ensure that neither long int nor
int contain padding.
 
C

Chris Thomasson

Tomás Ó hÉilidhe said:
Chris Thomasson:
____________________________________________________________
#include <stdio.h>

typedef char static_assert[
sizeof(long int) == sizeof(int) ? 1 : -1
];

typedef union foo_u {
long int a;
int b;
} foo;

int main(void) {
foo f = { 0 };
f.a = 1;
printf("%d\n", f.b);
return 0;
}


____________________________________________________________



You might want another static_assert to ensure that neither long int nor
int contain padding.

I just wanted to ensure that they were indeed equal in size in order to keep
the union semantics somewhat sane wrt the contrived example at hand.
 
P

Peter Nilsson

Ben Bacarisse said:
I thought this was a case where the last sentence of
6.5.6 p8 applies:
"If the result points one past the last element of the
array object, it shall not be used as the operand of a
unary * operator that is evaluated.".

That's a new one I wasn't aware of! I've asked the
question in comp.std.c.
 
B

Ben Bacarisse

Peter Nilsson said:
That's a new one I wasn't aware of! I've asked the
question in comp.std.c.

That's a good idea. I hope a consensus emerges. The construct seems
safe to me though, from my reading, currently undefined.

If the meaning of "evaluated" were to exclude cases where the
resulting lvalue decays to a pointer (which is reasonable, since no
actual access is implied in such cases) then the construct in question
would be well-defined.
 
R

Richard Bos

Chris Thomasson said:
I have always wondered why that rule exists. For instance:
typedef union foo_u {
long int a;
int b;
} foo;
foo f = { 0 };
f.a = 1;
printf("%d\n", f.b);

Possibly because it is hard to write a rule which reliably forbids the
kind of union hacks which would break, but allows the ones that
wouldn't, while the kind of aliasing hackery which one can do with
unions is also done, and more reliably, with pointer aliasing. Using
your example, you could just as easily have made f a long int, and
called for &(int *)&f. Some would call that ugly, but then, what you're
doing _is_ slightly ugly, and the pointer code expresses more directly
what you're doing.

Richard
 
K

Keith Thompson

Possibly because it is hard to write a rule which reliably forbids the
kind of union hacks which would break, but allows the ones that
wouldn't, while the kind of aliasing hackery which one can do with
unions is also done, and more reliably, with pointer aliasing. Using
your example, you could just as easily have made f a long int, and
called for &(int *)&f. Some would call that ugly, but then, what you're
doing _is_ slightly ugly, and the pointer code expresses more directly
what you're doing.

I think you meant *(int *)&f.

The problem with pointer aliasing is that it doesn't allow for
alignment. If f is a long int, then *(int *)&f can fail if f is not
aligned correctly as an int. (I can even imagine a plausible
implementation where ints require stricter alignment than long ints.)

Unions require all their members to be properly aligned.

Another alternative is to use memcpy() to copy the representation of
an object into another object of the desired type; this also avoids
alignment problems.
 
L

lawrence.jones

Richard Bos said:
Possibly because it is hard to write a rule which reliably forbids the
kind of union hacks which would break, but allows the ones that
wouldn't,

Nonetheless, that's exactly what C99 tried to do -- it removed the
prohibition against reading a member of a union other than the last one
assigned to and relies on the representation of types rules to specify
what's defined and what isn't. For example, given the union:

union u {
int i;
int j;
unsigned char a[sizeof (int)];
};

It's perfectly valid to store a value in i and then retrieve it from j
or to retrieve its object representation from a.

-Larry Jones

Sometimes I think the surest sign that intelligent life exists elsewhere
in the universe is that none of it has tried to contact us. -- Calvin
 
Y

ymuntyan

Possibly because it is hard to write a rule which reliably forbids the
kind of union hacks which would break, but allows the ones that
wouldn't, while the kind of aliasing hackery which one can do with
unions is also done, and more reliably, with pointer aliasing.

It's not true. Some TC explicitly made type punning using
unions work (unless you step onto a trap representation),
while pointer aliasing is UB. And here's UB in action:

muntyan@munt10:/tmp$ cat alias.c
#include <stdio.h>

int func (void)
{
int f = 9;
*(short*)&f = 2;
return f;
}

int main (void)
{
printf ("%d\n", func());
return 0;
}

muntyan@munt10:/tmp$ gcc -O2 -Wall alias.c
alias.c: In function 'func':
alias.c:6: warning: dereferencing type-punned pointer will break
strict-aliasing rules

muntyan@munt10:/tmp$ ./a.out
9

Yevgen
 
C

Chris Thomasson

you forgot to quote the following:

typedef char static_assert[
sizeof(long int) == sizeof(int) ? 1 : -1
];
 
D

David Thompson

user923005:
That void pointers have no stride, and yet you can do this:

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

int main(void)
{
void *p[5][5];
p[0][0] = NULL;
return 0;
}


"Have no stride"? As in "have no gait"? As in "don't have a particular way
of walking"?

Is this your way of saying you can't do pointer arithmetic on them?

Yes. In computing 'stride' has the somewhat specialized meaning of
'the distance from one array element to the next'. For contiguous
arrays, the only type in C, the stride of a pointer is (always) the
size of the array element(s) it points to. For other languages it can
(sometimes) be more complicated, but that's offtopic.

http://en.wikipedia.org/wiki/Stride_of_an_array (which in spite of the
oft-observed limitations of wikipedia is basically correct).

The type 'void' in C has no size, so a pointer to void has no stride
in standard C (although GNU C gives it one as an extension).

- formerly david.thompson1 || achar(64) || worldnet.att.net
 
D

David Thompson

[end_ptr] = my_array + sizeof my_array/sizeof*my_array;
[versus] = /* decay from */ * (&my_array+1);
[but the dereference in the latter nominally causes UB even if in
practice it will be optimized away and work as desired]

Concur.

Aside: I don't like pend for 'pointer to end' because it's also a
perfectly good word for 'wait', 'suspend', 'delay' and I tend to read
it as that. I prefer p_n or p_lim or something along those lines.
I can't see anything wrong with:

int *pend = (void *)(&my_array + 1);

from a language point of view, but it is fragile in that it breaks
when the code moves to a function and my_array becomes a pointer.
sizeof myarray / sizeof *myarray also breaks in that case.

You can put (void*)&array == (void*)array in somewhere to ensure it
really is an array and not a pointer -- but this reclutters the code,
which is what the OP wanted to avoid. Of course you can hide it in a
macro -- but hiding things in macros is a whole other controversy.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 
B

Ben Bacarisse

David Thompson said:
sizeof myarray / sizeof *myarray also breaks in that case.

You can put (void*)&array == (void*)array in somewhere to ensure it
really is an array and not a pointer

Very nice. I had not though of that.
 
D

David Thompson

On Fri, 8 Feb 2008 09:33:40 +0000 (UTC), "Herbert Rosenau"
<type> *p = NULL; /* legal */
<type> *p = 0; /* legal */
<type> *p = '\0'; /* legal */

memset(p, 0, n); /* illegal! when p is misused to overwrite vales not
type char */
(Presumably this is not the same p just set to a null pointer. said:
There is absolutely no guarantee that all bits 0 are a legal value for
all (except char, including pointer) types.

Nit: originally the all-zero-bits guarantee was only for _unsigned_
char. As of C99+TC2 it is for all integer types.

#include "usual/debate/about/whether/C99/is/used/enough"

Still no guarantee for floating-point in base C (although for the
common "IEEE"=60559 option it is) and pointers.
All bits 0 does NOT mean
that it is a nullpointer. An address (pointer) decays in 2 parts:
- address bits
- padding bits

So memset on anything, except char arrays, is always a good chance to
produce illegal padding bits.
I don't think I'd say a _good_ chance; implementations that use
significant padding bits, though allowed, are vanishingly rare, and
similarly for not using or at least allowing an all-zero-bits rep at
least for nullpointer. But it is _some_ chance.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top