Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > About casts (and pointers)

Reply
Thread Tools

About casts (and pointers)

 
 
sunglo@katamail.com
Guest
Posts: n/a
 
      04-18-2005
Some time a go, in a discussion here in comp.lang.c, I learnt that it's
better not to use a (sometype **) where a (void **) is expected (using
a cast). Part of the discussion boiled down to the rule: if I cast a
(sometype **) to a (void **) I am making a number of assumptions about
the implementation's (void **) representation and length. Specifically,
if I do the above cast I'm assuming that a (sometype **) and a (void
**) have the same size and representation, and this might not always be
true. Ok, all clear up to this point.

But now my question is: does the above rule generalize to *every*
possible cast (expecially those where pointers are involved)? Is every
explicit cast unsafe? (Here I'm not talking about conversions to/from
void *, which I know are safe if performed implicitly by the language.
I also know that casts are better avoided unless strictly needed. But
I'm curious to know how things work).

As an example, look at the widely used cast (struct sockaddr_in *) to
(struct sockaddr *). Is that safe?
Seems to me that, as in the case of (void **), that cast assumes that
the size and representation of a (struct sockaddr *) are the same of a
(struct sockaddr_in *). Should this be done using an intermediate (void
*)?

What is (if any) the general rule? Or, in general, what can be said and
assumed about the casted object?

Thanks and sorry for the possibly silly questions.

 
Reply With Quote
 
 
 
 
Eric Sosman
Guest
Posts: n/a
 
      04-18-2005


http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:
> Some time a go, in a discussion here in comp.lang.c, I learnt that it's
> better not to use a (sometype **) where a (void **) is expected (using
> a cast). Part of the discussion boiled down to the rule: if I cast a
> (sometype **) to a (void **) I am making a number of assumptions about
> the implementation's (void **) representation and length. Specifically,
> if I do the above cast I'm assuming that a (sometype **) and a (void
> **) have the same size and representation, and this might not always be
> true. Ok, all clear up to this point.


Almost clear, but still slightly murky. When you (try
to) use a `sometype **' and a `void **' interchangeably, the
assumption is that `sometype *' (one asterisk) and `void *'
have the same representation.

> But now my question is: does the above rule generalize to *every*
> possible cast (expecially those where pointers are involved)? Is every
> explicit cast unsafe? (Here I'm not talking about conversions to/from
> void *, which I know are safe if performed implicitly by the language.
> I also know that casts are better avoided unless strictly needed. But
> I'm curious to know how things work).


Some explicit casts are safe, some are not. For example,
any data pointer can be converted to `unsigned char *' and
then used to access the individual bytes of the original object;
this it perfectly safe (although what you actually do to the
bytes might not be).

Honesty is the best policy. If you've got an object
of type `sometype', use a `sometype *' to point to it.
If you've got an object of type `sometype *', point to it
with a `sometype **'. If that's not possible (for example,
when writing a qsort() comparison function), then converting
a data pointer to `void *' (one asterisk) and back is all
right. Other pointer conversions should be viewed with
suspicion, although not necessarily with horror.

> As an example, look at the widely used cast (struct sockaddr_in *) to
> (struct sockaddr *). Is that safe?
> Seems to me that, as in the case of (void **), that cast assumes that
> the size and representation of a (struct sockaddr *) are the same of a
> (struct sockaddr_in *). Should this be done using an intermediate (void
> *)?


Ah, yes, well, you've encountered a special case. If
you've got two struct types that begin with the same sequence
of elements, then you can use a pointer to either type to get
at those initial elements. A `struct sockaddr' (presumably)
starts with a few elements that let the called function
decide whether it's been given a `struct sockaddr_in' or a
`struct sockaddr_something_else', and thereafter the called
function can convert the pointer to the proper struct type
so as to access the remaining elements.

Fans of object-oriented languages sneer at this "poor
man's polymorphism," and some of their sneers are perhaps
justified: it works, but it's fragile in the sense that the
compiler usually cannot warn you about simple errors. If you
must use an API that indulges in this sort of thing -- well,
that's what the API demands, and you haven't a lot of choice.
When designing your own functions, though, I'd suggest you avoid
this practice unless you find compelling reasons to adopt it.

--
(E-Mail Removed)

 
Reply With Quote
 
 
 
 
Christian Bau
Guest
Posts: n/a
 
      04-18-2005
In article <d41cea$4gf$(E-Mail Removed)>,
Eric Sosman <(E-Mail Removed)> wrote:

> Ah, yes, well, you've encountered a special case. If
> you've got two struct types that begin with the same sequence
> of elements, then you can use a pointer to either type to get
> at those initial elements. A `struct sockaddr' (presumably)
> starts with a few elements that let the called function
> decide whether it's been given a `struct sockaddr_in' or a
> `struct sockaddr_something_else', and thereafter the called
> function can convert the pointer to the proper struct type
> so as to access the remaining elements.


Strictly speaking, this is only true if you define a union containing a
"struct sockaddr_in" and a "struct sockaddr_something_else", and the
compiler must have seen the declaration of that union before your code
handles "struct sockaddr_in" and "struct sockaddr_something_else"
interchangably.

In practice, it will always work except perhaps on the DeathStation 9000
because it would be quite difficult for a compiler to make it work when
the compiler is forced to make it work but not in other cases.
 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      04-19-2005
Christian Bau wrote:

> In article <d41cea$4gf$(E-Mail Removed)>,
> Eric Sosman <(E-Mail Removed)> wrote:
>
>> Ah, yes, well, you've encountered a special case. If
>>you've got two struct types that begin with the same sequence
>>of elements, then you can use a pointer to either type to get
>>at those initial elements. A `struct sockaddr' (presumably)
>>starts with a few elements that let the called function
>>decide whether it's been given a `struct sockaddr_in' or a
>>`struct sockaddr_something_else', and thereafter the called
>>function can convert the pointer to the proper struct type
>>so as to access the remaining elements.

>
> Strictly speaking, this is only true if you define a union containing a
> "struct sockaddr_in" and a "struct sockaddr_something_else", and the
> compiler must have seen the declaration of that union before your code
> handles "struct sockaddr_in" and "struct sockaddr_something_else"
> interchangably.


Yeah, I thought about the "in a union" thing when composing
my post, but decided to ignore it. As far as I can tell, the
compiler could only behave perversely if it could somehow prove
that that the two structs could never possibly appear as members
of the same union, even in translation units the compiler has not
yet seen that might or might not be linked into the same final
program. I believe such a proof is beyond the capabilities of
current compilers, and is likely to remain so until I die and no
longer care about it ...

> In practice, it will always work except perhaps on the DeathStation 9000
> because it would be quite difficult for a compiler to make it work when
> the compiler is forced to make it work but not in other cases.


Perversity is always a possibility. It's not very marketable,
though, outside the realm of popular "music."

--
Eric Sosman
(E-Mail Removed)
 
Reply With Quote
 
Daniel Vallstrom
Guest
Posts: n/a
 
      04-19-2005
(E-Mail Removed) wrote:
> Some time a go, in a discussion here in comp.lang.c, I learnt that

it's
> better not to use a (sometype **) where a (void **) is expected

(using
> a cast). Part of the discussion boiled down to the rule: if I cast a
> (sometype **) to a (void **) I am making a number of assumptions

about
> the implementation's (void **) representation and length.

Specifically,
> if I do the above cast I'm assuming that a (sometype **) and a (void
> **) have the same size and representation, and this might not always

be
> true. Ok, all clear up to this point.
>
> But now my question is: does the above rule generalize to *every*
> possible cast (expecially those where pointers are involved)? Is

every
> explicit cast unsafe? (Here I'm not talking about conversions to/from
> void *, which I know are safe if performed implicitly by the

language.

The standard defines exact width integer types [u]intN_t in a strict
way saying that there must be no padding etc. As a result, IMO/AFAIK
it's safe to convert a pointer from one [u]intN_t type to a different
[u]intM_t type. For example, the following is safe:

uint32_t * a; ...
uint8_t * b = a;
b[3] = b[5];
a = b+4;
a[7] = a[9];

(On the other hand, the [u]intN_t types need not be supported of
course.)

Daniel Vallstrom

 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      04-19-2005
"Daniel Vallstrom" <(E-Mail Removed)> writes:
[...]
> The standard defines exact width integer types [u]intN_t in a strict
> way saying that there must be no padding etc.


Yes.

> As a result, IMO/AFAIK
> it's safe to convert a pointer from one [u]intN_t type to a different
> [u]intM_t type.


How does that follow?

> For example, the following is safe:
>
> uint32_t * a; ...
> uint8_t * b = a;


There is no implicit conversion from uint32_t* to uint8_t*, so you
need an explicit cast here (though some compilers may allow it).

uint8_t is a special case because, if it exists, it's very likely
(certain?) to be a typedef for unsigned char. But let's change the
example to:

uint32_t arr[10];
uint32_t *a = arr;
uint16_t *b = a;

You can safely convert a pointer to one type to a pointer to another
type *and back again* if the intermediate pointer is correctly
aligned; if it isn't, the conversion invokes undefined behavior. It's
likely that int32_t has stricter alignment requirements than int16_t;
if so, converting from int16_t* to int32_t* can invoke undefined
behavior. (It's possible, but unlikely in reality, that int16_t has
stricter alignment requirements than int32_t.)

Even if the conversion is allowed, the result isn't necessarily going
to be useful.

--
Keith Thompson (The_Other_Keith) (E-Mail Removed) <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
 
Reply With Quote
 
Daniel Vallstrom
Guest
Posts: n/a
 
      04-19-2005
Keith Thompson wrote:
> "Daniel Vallstrom" <(E-Mail Removed)> writes:
> [...]
> > The standard defines exact width integer types [u]intN_t in a

strict
> > way saying that there must be no padding etc.

>
> Yes.
>
> > As a result,

IMO/AFAIK
> > it's safe to convert a pointer from one [u]intN_t type to a

different
> > [u]intM_t type.

>
> How does that follow?


It doesn't. (It would if pointers were addresses.)


> > For example, the following is safe:
> >
> > uint32_t * a; ...
> > uint8_t * b = a;

>
> There is no implicit conversion from uint32_t* to uint8_t*, so you
> need an explicit cast here (though some compilers may allow it).


Right.

> uint8_t is a special case because, if it exists, it's very likely
> (certain?) to be a typedef for unsigned char.


Right. Change 8 to 16.

> But let's change the
> example to:


Let's not I'll instead apologies for a very poor post riddled
with errors and try again with a proper example:

This, I believe, should be safe --- even though it's rather pointless:

int16_t * a = malloc( sizeof *a * 10 ); ...
int32_t * b = (int32_t*)(a+2);
int16_t * c = (int16_t*)b - 2;
assert( a == c );


> You can safely convert a pointer to one type to a pointer to another
> type *and back again* if the intermediate pointer is correctly
> aligned; if it isn't, the conversion invokes undefined behavior.

It's
> likely that int32_t has stricter alignment requirements than int16_t;
> if so, converting from int16_t* to int32_t* can invoke undefined
> behavior. (It's possible, but unlikely in reality, that int16_t has
> stricter alignment requirements than int32_t.)
>
> Even if the conversion is allowed, the result isn't necessarily going
> to be useful.


Right. Thanks for all the corrections.


Daniel Vallstrom

 
Reply With Quote
 
Chris Croughton
Guest
Posts: n/a
 
      04-19-2005
On 19 Apr 2005 04:31:03 -0700, Daniel Vallstrom
<(E-Mail Removed)> wrote:

> This, I believe, should be safe --- even though it's rather pointless:
>
> int16_t * a = malloc( sizeof *a * 10 ); ...
> int32_t * b = (int32_t*)(a+2);
> int16_t * c = (int16_t*)b - 2;
> assert( a == c );


No, because the intX_t types are guaranteed only to be of a type with at
least X bits. An int16_t could have 16 bits and an int32_t 64 bits, for
instance. Or an int32_t might have to be aligned at 8 byte boundaries
but an int16_t only need to be aligned at 1 byte boundaries. Even
setting a pointer to the type might result in a trap.

A pathological but possible implementation:

char (int8_t) is 12 bits (1 byte)
short (int16_t) is 24 bits (2 bytes), aligned on a 2-byte boundary
int (int32_t) is 36 bits (3 bytes), aligned on a 3-byte boundary.

Your code would generate b as (char*)a + 4, which is not on the 3-byte
boundary required for an int. The compiler could legitimately round the
address up (or down) when converting to an int32_t* (it could also
launch ICBMs at the White House, cause a plague of frogs or any other
undefined behaviour) and converting it back to an int16_t* could do the
same...

You can convert a pointer to any type to a pointer to any other type,
providing that its alignment is satisfactory. I can find no guarantee
that doing pointer arithmetic on it and converting it back will result
in anything defined.

Chris C
 
Reply With Quote
 
Richard Bos
Guest
Posts: n/a
 
      04-19-2005
Chris Croughton <(E-Mail Removed)> wrote:

> On 19 Apr 2005 04:31:03 -0700, Daniel Vallstrom
> <(E-Mail Removed)> wrote:
>
> > This, I believe, should be safe --- even though it's rather pointless:
> >
> > int16_t * a = malloc( sizeof *a * 10 ); ...
> > int32_t * b = (int32_t*)(a+2);
> > int16_t * c = (int16_t*)b - 2;
> > assert( a == c );

>
> No, because the intX_t types are guaranteed only to be of a type with at
> least X bits. An int16_t could have 16 bits and an int32_t 64 bits, for
> instance. Or an int32_t might have to be aligned at 8 byte boundaries
> but an int16_t only need to be aligned at 1 byte boundaries. Even
> setting a pointer to the type might result in a trap.


Nope.

# The typedef name intN_t designates a signed integer type with width N,
# no padding bits, and a two’s complement representation. Thus, int8_t
# denotes a signed integer type with a width of exactly 8 bits.

(7.18.1.1#1)

You're thinking of int_leastN_t.

Richard
 
Reply With Quote
 
Mark Piffer
Guest
Posts: n/a
 
      04-19-2005
Eric Sosman wrote:
> (E-Mail Removed) wrote:
> > As an example, look at the widely used cast (struct sockaddr_in *)

to
> > (struct sockaddr *). Is that safe?
> > Seems to me that, as in the case of (void **), that cast assumes

that
> > the size and representation of a (struct sockaddr *) are the same

of a
> > (struct sockaddr_in *). Should this be done using an intermediate

(void
> > *)?

>
> Ah, yes, well, you've encountered a special case. If
> you've got two struct types that begin with the same sequence
> of elements, then you can use a pointer to either type to get
> at those initial elements. A `struct sockaddr' (presumably)
> starts with a few elements that let the called function
> decide whether it's been given a `struct sockaddr_in' or a
> `struct sockaddr_something_else', and thereafter the called
> function can convert the pointer to the proper struct type
> so as to access the remaining elements.


To add another question to this special case:
the standard guarantees in 6.2.5#26 that (struct sockaddr *) and
(struct sockaddr_in *) are of the same representation and alignment,
which would allow us to assume that (struct sockaddr **) and (struct
sockaddr_in **) are portably convertible into each other (and usable
afterwards) as they are pointing to mutually correctly aligned
locations. Is this reasoning correct?

Mark

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
checking casts Dan Upton Java 4 12-01-2005 06:20 PM
Web casts in ASP.Net =?Utf-8?B?Q2hyaXMgRGF2b2xp?= ASP .Net 1 10-19-2005 09:45 PM
Epson colour casts on duotones? Bruce Robbins Digital Photography 5 05-25-2004 03:41 AM
Needless casts? Joona I Palaste Java 15 04-25-2004 10:14 PM
Re: = operator should automatically perform appropriate casts cgbusch Java 2 07-08-2003 03:58 PM



Advertisments