[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Converting between object pointers

Lauri Alanko

6/29/2011 10:48:00 AM

The standard (6.3.2.3#7) makes conversion between object types
well-defined when a converted pointer is "correctly aligned" for the
referenced type. But in what situations can a portable program trust
that a pointer of type A* is correctly aligned for conversion to B*?
Certainly when B is a character type (or void), or when the A* pointer
has been converted from the return value of a malloc call. But are
there any other cases?

How about the case when B is an incomplete type? Is the following
guaranteed to work?

typedef struct A { int x; } A;
typedef struct B B;
A a = { 42 };
B* bp = (B*) &a;
A* ap = (A*) bp;
ap->x = 54;

I can't see how this could go wrong, but could it be that B (which is
never ever completed) could somehow have stricter alignment
requirements than A, making the conversion undefined?

Thanks,


Lauri
19 Answers

James Kuyper

6/29/2011 12:55:00 PM

0

On 06/29/2011 06:48 AM, Lauri Alanko wrote:
> The standard (6.3.2.3#7) makes conversion between object types
> well-defined when a converted pointer is "correctly aligned" for the
> referenced type. But in what situations can a portable program trust
> that a pointer of type A* is correctly aligned for conversion to B*?
> Certainly when B is a character type (or void), or when the A* pointer
> has been converted from the return value of a malloc call. But are
> there any other cases?

An array type must have an alignment requirement at least as strict as
that of the element type of the array.
A struct must have an alignment requirement that is a common multiple of
the alignment requirements of all of it's members; otherwise arrays of
structs wouldn't work properly. Therefore, a pointer to any array is
correctly aligned for any of the types it has as a member.
A union must be correctly aligned for any of it's member types.
All struct pointers have the same alignment requirement.

> How about the case when B is an incomplete type? Is the following
> guaranteed to work?
>
> typedef struct A { int x; } A;
> typedef struct B B;
> A a = { 42 };
> B* bp = (B*) &a;
> A* ap = (A*) bp;
> ap->x = 54;

No, and no.

> I can't see how this could go wrong, but could it be that B (which is
> never ever completed) could somehow have stricter alignment
> requirements than A, making the conversion undefined?

Yes. One way in which it could go wrong is that the conversion to B*
might round the address to the nearest location that is correctly
aligned for a struct B object, which might not be the location where 'a'
resides.
--
James Kuyper

Shao Miller

6/29/2011 2:26:00 PM

0

On 6/29/2011 06:48, Lauri Alanko wrote:
> The standard (6.3.2.3#7) makes conversion between object types
> well-defined when a converted pointer is "correctly aligned" for the
> referenced type. But in what situations can a portable program trust
> that a pointer of type A* is correctly aligned for conversion to B*?

If 'A' is an aggregate or union type which has a 'B' subobject at its
beginning, then a valid 'A *' pointer value can be converted with
confidence to a 'B *'.

As an example:

union B {
int w;
double d;
};

struct middle {
union B b;
char c[10];
};

union A {
int w;
struct middle m;
};

'A' is a union type. Its member 'm' is at its beginning (all members of
a union are). 'm' is a struct type. Its member 'b' is at its
beginning. Thus the 'A' type has a 'B' member at its beginning and a
valid 'A *' value can be converted to a 'B *' value.

If the size and alignment are unknown, I don't think you have any
guarantee about the conversion's behaviour.

> Certainly when B is a character type (or void), or when the A* pointer
> has been converted from the return value of a malloc call.

Right. "...suitably aligned so that it may be assigned to a pointer to
any type of object..."[7.20.3p1]

> But are
> there any other cases?
>

Hmmm... Do you know the size and/or alignment for the type?

> How about the case when B is an incomplete type? Is the following
> guaranteed to work?
>
> typedef struct A { int x; } A;
> typedef struct B B;
> A a = { 42 };
> B* bp = (B*)&a;
> A* ap = (A*) bp;
> ap->x = 54;
>

I don't think so, unless 'B' is completed and you find that its
alignment requirement is <= 'A'.

In C1X, we have the 'alignof' operator and some other alignment friends.

Mr. C. M. Thomasson has offered an 'ALIGNOF' macro that goes something like:

#define ALIGNOF(type) (offsetof(struct { char c; type m; }, m))

Which seems very nice to me, except for a couple of minor weaknesses.

Both the C1X operator as well as this macro require that the type is a
completed object type.

> I can't see how this could go wrong, but could it be that B (which is
> never ever completed) could somehow have stricter alignment
> requirements than A, making the conversion undefined?

I believe it could be the case, yes. It seems odd to think that such a
determination can ever be made if the type is never completed.

Do you have a specific goal that you are trying to accomplish, or are
you just curious? Perhaps there are other Standard guarantees which
might apply to some situation you might be thinking about.

Lauri Alanko

6/29/2011 4:24:00 PM

0

In article <iufcld$fgi$1@dont-email.me>,
Shao Miller <sha0.miller@gmail.com> wrote:
> In C1X, we have the 'alignof' operator and some other alignment friends.
>
> Mr. C. M. Thomasson has offered an 'ALIGNOF' macro that goes something like:
>
> #define ALIGNOF(type) (offsetof(struct { char c; type m; }, m))
>
> Which seems very nice to me, except for a couple of minor weaknesses.
>
> Both the C1X operator as well as this macro require that the type is a
> completed object type.

I'm aware of these, but these are only useful for runtime checks, and
here I was interested in static properties of types.

(Also, the usefulness of a standard alignof seems limited unless we
also have a standard way of manipulating addresses to ensure their
alignment, and to my understanding the new standard doesn't provide
this, only aligned_alloc. The common approach of manipulating the
lower bits of a pointer-turned-int is still implementation-defined.)

> Do you have a specific goal that you are trying to accomplish, or are
> you just curious? Perhaps there are other Standard guarantees which
> might apply to some situation you might be thinking about.

I was considering one approach to the age-old "opaque typedef"
problem. Say I have a type for dictionaries:

typedef struct Dict Dict;
Dict* dict_new(....);
void* dict_get(Dict* dict, void* key);

Now I want to have a separate type IntDict for dicts whose keys are
ints. I can use the implementation for Dict, I just want a distinct
type to ensure that other Dicts can't be passed to IntDict functions.
One approach is this:

typedef struct IntDict IntDict;

inline IntDict* intdict_new(....) {
return (IntDict*) dict_new(....);
}

inline void* intdict_get(IntDict* idict, int key) {
return dict_get((Dict*) idict, &key);
}

This seemed appealing to me, because we get a distinct type, and
IntDict* is directly a pointer type which is convenient for its
void*-compatibility.

But if the above doesn't work, I'm forced to resort to struct-wrapping
the actual pointer:

typedef struct IntDictP {
Dict* dict_;
} IntDictP;

inline IntDictP intdict_new(....) {
return (IntDictP) { dict_new(....) };
}

inline void* intdict_get(IntDictP idict, int key) {
return dict_get(idict.dict_, &key);
}

I think this is uglier, since now the user of the intdict API has to
use a special handle type instead of an ordinary pointer. But perhaps
this is then the only portable way to do this.


Lauri

Chris M. Thomasson

6/29/2011 6:41:00 PM

0

"Lauri Alanko" <la@iki.fi> wrote in message
news:iufji8$crf$1@oravannahka.helsinki.fi...
> In article <iufcld$fgi$1@dont-email.me>,
> Shao Miller <sha0.miller@gmail.com> wrote:
>> In C1X, we have the 'alignof' operator and some other alignment friends.
>>
>> Mr. C. M. Thomasson has offered an 'ALIGNOF' macro that goes something
>> like:
>>
>> #define ALIGNOF(type) (offsetof(struct { char c; type m; }, m))
>>
>> Which seems very nice to me, except for a couple of minor weaknesses.
>>
>> Both the C1X operator as well as this macro require that the type is a
>> completed object type.
>
> I'm aware of these, but these are only useful for runtime checks, and
> here I was interested in static properties of types.

FWIW, the `ALIGNOF()' function macro allows one to determine a compatible
alignment for a given type at compilation time as well.

[...]


Chris M. Thomasson

6/29/2011 6:43:00 PM

0

"Lauri Alanko" <la@iki.fi> wrote in message
news:iufji8$crf$1@oravannahka.helsinki.fi...
> In article <iufcld$fgi$1@dont-email.me>,
> Shao Miller <sha0.miller@gmail.com> wrote:
>> In C1X, we have the 'alignof' operator and some other alignment friends.
>>
>> Mr. C. M. Thomasson has offered an 'ALIGNOF' macro that goes something
>> like:
>>
>> #define ALIGNOF(type) (offsetof(struct { char c; type m; }, m))
>>
>> Which seems very nice to me, except for a couple of minor weaknesses.
>>
>> Both the C1X operator as well as this macro require that the type is a
>> completed object type.
>
> I'm aware of these, but these are only useful for runtime checks, and
> here I was interested in static properties of types.
>
> (Also, the usefulness of a standard alignof seems limited unless we
> also have a standard way of manipulating addresses to ensure their
> alignment, and to my understanding the new standard doesn't provide
> this, only aligned_alloc. The common approach of manipulating the
> lower bits of a pointer-turned-int is still implementation-defined.)

How safe is something like this:

void* align_ptr(void* ptr, size_t align)
{
if (align > 1) {
/* assume align is power of two */
size_t base = ((unsigned char*)ptr) - 0UL;
size_t align_mask = align - 1;
size_t align_addr = (base + align_mask) & ~align_mask;
size_t align_inc = align_addr - base;
unsigned char* align_base = ((unsigned char*)ptr) + align_inc;
assert(! (align_addr % align));
return align_base;
}

return ptr;
}

?


Is there a much "safer" way to do this?

Thanks!

[...]


Tim Rentsch

6/29/2011 7:32:00 PM

0

Lauri Alanko <la@iki.fi> writes:

> The standard (6.3.2.3#7) makes conversion between object types
> well-defined when a converted pointer is "correctly aligned" for the
> referenced type. But in what situations can a portable program trust
> that a pointer of type A* is correctly aligned for conversion to B*?
> Certainly when B is a character type (or void), or when the A* pointer
> has been converted from the return value of a malloc call. But are
> there any other cases?
>
> How about the case when B is an incomplete type? Is the following
> guaranteed to work?
>
> typedef struct A { int x; } A;
> typedef struct B B;
> A a = { 42 };
> B* bp = (B*) &a;
> A* ap = (A*) bp;
> ap->x = 54;
>
> I can't see how this could go wrong, but could it be that B (which is
> never ever completed) could somehow have stricter alignment
> requirements than A, making the conversion undefined?

Practically speaking it is very unlikely to fail.

However, the Standard does allow it to fail, and it's easy
to see how to construct an implementation where it would
fail. Suppose we have an implementation where structs whose
structure tag starts with 'A' always have an alignment that
is a multiple of 16, and structs whose structure tag starts
with 'B' always have an alignment that is a multiple of 32.
(It's easy to do this just by adding enough padding.)
Furthermore suppose our implementation is "helpful" in that
after a pointer conversion it checks the converted pointer
to make sure it is suitably aligned, and aborts the program
if it isn't. All these things are allowed by the Standard,
and easily could cause the above program to misbehave, if
the 'a' structure is 16-aligned but not 32-aligned.

For trying to decide if something can fail, the "malicious
implementation" approach usually is pretty good at showing
that something can fail, if indeed the Standard does allow it
to fail.

Shao Miller

6/29/2011 7:37:00 PM

0

On 6/29/2011 12:23, Lauri Alanko wrote:
> In article<iufcld$fgi$1@dont-email.me>,
> Shao Miller<sha0.miller@gmail.com> wrote:
>> In C1X, we have the 'alignof' operator and some other alignment friends.
>>
>> Mr. C. M. Thomasson has offered an 'ALIGNOF' macro that goes something like:
>>
>> #define ALIGNOF(type) (offsetof(struct { char c; type m; }, m))
>>
>> Which seems very nice to me, except for a couple of minor weaknesses.
>>
>> Both the C1X operator as well as this macro require that the type is a
>> completed object type.
>
> I'm aware of these, but these are only useful for runtime checks, and
> here I was interested in static properties of types.
>

I'm not sure I grok "only useful for runtime checks." Both result in
integer constant expressions and so are suitable for such things as
enumeration values or array sizes; possible translation-time determinations.

> (Also, the usefulness of a standard alignof seems limited unless we
> also have a standard way of manipulating addresses to ensure their
> alignment, and to my understanding the new standard doesn't provide
> this, only aligned_alloc. The common approach of manipulating the
> lower bits of a pointer-turned-int is still implementation-defined.)
>

C1X has the '_Alignas' keyword as an "alignment specifier." I don't
know of a C89/C99 counterpart.

With '*alloc'-allocated storage, you can perform your own computations
to ensure any objects stored therein adhere to whatever alignment
requirements you want. My 'pfxalloc' example in another thread provides
an example (though it could certainly use improvement).

>> Do you have a specific goal that you are trying to accomplish, or are
>> you just curious? Perhaps there are other Standard guarantees which
>> might apply to some situation you might be thinking about.
>
> I was considering one approach to the age-old "opaque typedef"
> problem.

By co-incidence, I was pondering this one last night. :)

> Say I have a type for dictionaries:
>
> typedef struct Dict Dict;
> Dict* dict_new(....);
> void* dict_get(Dict* dict, void* key);
>
> Now I want to have a separate type IntDict for dicts whose keys are
> ints. I can use the implementation for Dict, I just want a distinct
> type to ensure that other Dicts can't be passed to IntDict functions.

If I understand your requirements correctly, you wish to have another
"pointerish" type that can point at a 'Dict' but is nonetheless distinct
from 'Dict *'; not compatible.

> One approach is this:
>
> typedef struct IntDict IntDict;
>
> inline IntDict* intdict_new(....) {
> return (IntDict*) dict_new(....);
> }
>
> inline void* intdict_get(IntDict* idict, int key) {
> return dict_get((Dict*) idict,&key);
> }
>
> This seemed appealing to me, because we get a distinct type, and
> IntDict* is directly a pointer type which is convenient for its
> void*-compatibility.
>

Hmmm... If someone has a 'Dict *' value converted to a 'void *' value,
what'll stop them from passing that to 'intdict_get', regardless of the
distinct 'IntDict *' type?

> But if the above doesn't work, I'm forced to resort to struct-wrapping
> the actual pointer:
>

Not necessarily.

> typedef struct IntDictP {
> Dict* dict_;
> } IntDictP;
>
> inline IntDictP intdict_new(....) {
> return (IntDictP) { dict_new(....) };
> }
>
> inline void* intdict_get(IntDictP idict, int key) {
> return dict_get(idict.dict_,&key);
> }
>
> I think this is uglier, since now the user of the intdict API has to
> use a special handle type instead of an ordinary pointer. But perhaps
> this is then the only portable way to do this.

Are they going to be passing a 'void *' or an 'xxx' for the most part?

Are you trying to prevent API users from ever including any of the
definition of 'Dict'? If not, what about using array types with
something like:

/**** dictp.h */
#ifdef DICTP_H_OK_
struct s_dict_ {
double d;
};
typedef struct s_dict_ a_int_dict_[1][1][1];
#undef DICTP_H_OK_
#endif /* DICTP_H_OK_ */



/**** dict.h */
#ifndef DICT_H_

/***
* Private details
* API users shouldn't include this header directly
*/
#define DICTP_H_OK_
#include "dictp.h"

/*** Object types */
typedef struct s_dict_ Dict;
typedef a_int_dict_ IntDict;

/*** Function declarations */
extern Dict * NewDict(void);
extern void * GetDict(Dict *, void *);
extern IntDict * NewIntDict(void);
extern void * GetIntDict(IntDict *, int);

#endif /* DICT_H_ */



/**** dict.c */
#include <stdlib.h>
#include "dict.h"

Dict * NewDict(void) {
return malloc(sizeof (Dict));
}

void * GetDict(Dict * dict, void * key) {
/* Whatever */
return 0;
}

IntDict * NewIntDict(void) {
return (IntDict *) NewDict();
}

void * GetIntDict(IntDict * idict, int key) {
/* Whatever */
return 0;
}



/**** Test program */
#include <stdlib.h>
#include "dict.h"

int main(void) {
Dict * foo = NewDict();
IntDict * bar = NewIntDict();
free(foo);
free(bar);
return 0;
}

Chris M. Thomasson

6/29/2011 7:58:00 PM

0

"Shao Miller" <sha0.miller@gmail.com> wrote in message
news:iufusl$lvb$1@dont-email.me...
> On 6/29/2011 12:23, Lauri Alanko wrote:
[...]

> C1X has the '_Alignas' keyword as an "alignment specifier." I don't know
> of a C89/C99 counterpart.
>
> With '*alloc'-allocated storage, you can perform your own computations to
> ensure any objects stored therein adhere to whatever alignment
> requirements you want. My 'pfxalloc' example in another thread provides
> an example (though it could certainly use improvement).

This one right?

http://groups.google.com/group/comp.lang.c/msg/9c4007...


I must be missing something important; when I do this:
________________________________________
unsigned char* foo = pfxalloc(16, 8192, 128);
________________________________________


The pointer I get back to the 128-byte object is not aligned on a 8192
boundary.


[...]


Shao Miller

6/29/2011 8:04:00 PM

0

On 6/29/2011 14:42, James wrote:
>
> How safe is something like this:
>
> void* align_ptr(void* ptr, size_t align)
> {
> if (align> 1) {
> /* assume align is power of two */

No need to assume. You can check:

/* Sanity-check alignment value */
assert(!(align & (align - 1)));

> size_t base = ((unsigned char*)ptr) - 0UL;

What is 'base' supposed to hold at this point? It looks you are
assigning an 'unsigned char *' value to a 'size_t' object. Does that
compile?

> [...more code...]
>
> Is there a much "safer" way to do this?

To do what? Produce a particularly-aligned address? As far as I know,
only '*alloc'-allocated storage is guaranteed to begin at an address
that is suitably aligned for any object. So then the only alignment
management you can do is relative to such an an address, and your
pointer arithmetic must not go out-of-bounds for the allocated storage.

Shao Miller

6/29/2011 8:09:00 PM

0

On 6/29/2011 15:58, Chris M. Thomasson wrote:
>
> This one right?
>
> http://groups.google.com/group/comp.lang.c/msg/9c4007...
>
>
> I must be missing something important; when I do this:
> ________________________________________
> unsigned char* foo = pfxalloc(16, 8192, 128);
> ________________________________________
>
>
> The pointer I get back to the 128-byte object is not aligned on a 8192
> boundary.

It's relative to the start of the allocated storage. Is the start of
the allocated storage ('pfxget') at an 8,192-byte boundary?