Casting a byte array to allow assignment

Q

quantumred

I found the following code floating around somewhere and I'd like to
get some comments.

unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;

// now a1[0]=a2[0], a1[1]=a2[1], etc.

This is taking a byte array, casting it to a 4 byte integer pointer,
then dereferencing it, then assigning it. This results in the contents
of array a1 copied to array a2. (corrections to my interpretation
welcome)

I don't know why this was done this way. My first instinct would have
been to use memcpy. Is this faster than memcpy? Are there any dangers
other than the potential for int not being 4 bytes?
 
P

pete

quantumred said:
I found the following code floating around somewhere and I'd like to
get some comments.

unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;

// now a1[0]=a2[0], a1[1]=a2[1], etc.

This is taking a byte array, casting it to a 4 byte integer pointer,
then dereferencing it, then assigning it. This results in the contents
of array a1 copied to array a2. (corrections to my interpretation
welcome)

I don't know why this was done this way. My first instinct would have
been to use memcpy. Is this faster than memcpy? Are there any dangers
other than the potential for int not being 4 bytes?

There's the possibility that a1 and a2 don't
have the proper alignment for type unsigned.
 
A

Aaron Gage

probably a little quicker then memcpy, no function call overhead.

prob. safe if you know how your compiler treats these types, or you can
set appropriate command line params to your compiler (or use some
pragmas)
to force alignment and so on.

Not really worth the trouble i suspect though: not at all clear what is
happening, and
what you gonna save? a few instructions on a P4/Athlon/etc is nothin!
 
F

Flash Gordon

Aaron Gage wrote:

Please quote what you are replying to, Google now makes this easy. See
the last link in my sig for more information.

The code in question was:
quantumred said:
I found the following code floating around somewhere and I'd like to
get some comments.

unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;

// now a1[0]=a2[0], a1[1]=a2[1], etc.

This is taking a byte array, casting it to a 4 byte integer pointer,
then dereferencing it, then assigning it. This results in the contents
of array a1 copied to array a2. (corrections to my interpretation
welcome)

I don't know why this was done this way. My first instinct would have
been to use memcpy. Is this faster than memcpy? Are there any dangers
other than the potential for int not being 4 bytes?
probably a little quicker then memcpy, no function call overhead.

On most modern compiler there is probably no difference in speed.
prob. safe if you know how your compiler treats these types, or you can
set appropriate command line params to your compiler (or use some
pragmas)
to force alignment and so on.

Definitely *not* safe. It might work on the OPs specific platform, but
it is not safe because the code might then get reused on a system where
it does not work.
Not really worth the trouble i suspect though: not at all clear what is
happening, and
what you gonna save? a few instructions on a P4/Athlon/etc is nothin!

Definitely not worth the trouble.
 
F

Frederick Gotham

quantumred posted:
unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;


This will work perfectly if:

(1) 4 == sizeof(int)

(2) alignof(char[4]) >= alignof(int)


The C Standard does not provide these guarantees for all platforms and
implementations. What you have is non-portable, platform-specific code.

It seems to me that the author was aiming to optimise the copying of an
array. I'd probably take a route somewhat akin to the following:

#define DEFINE_SOLE_MEMBER(Type) \
\
struct SoleMember##Type { \
Type obj; \
} /* Usage code provides semi-colon */ \

#define SoleMember(Type) SoleMember##Type

int main()
{
unsigned char a1[4]= {5,10,15,20};
unsigned char a2[4]= {25,30,35,40};

typedef char char4[4];

DEFINE_SOLE_MEMBER(char4);

*(SoleMember(char4)*)a1 = *(SoleMember(char4) const*)a2;
}


(The Standard guarantees that you can cast from a pointer to a struct, to a
pointer to the type of its first member. However, I don't believe it
guarantees the reverse -- so the code might be slightly dubious.)
 
E

Eric Sosman

Frederick said:
quantumred posted:

unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;



This will work perfectly if:

(1) 4 == sizeof(int)

(2) alignof(char[4]) >= alignof(int)


The C Standard does not provide these guarantees for all platforms and
implementations. What you have is non-portable, platform-specific code.

It seems to me that the author was aiming to optimise the copying of an
array. I'd probably take a route somewhat akin to the following:

#define DEFINE_SOLE_MEMBER(Type) \
\
struct SoleMember##Type { \
Type obj; \
} /* Usage code provides semi-colon */ \

#define SoleMember(Type) SoleMember##Type

int main()
{
unsigned char a1[4]= {5,10,15,20};
unsigned char a2[4]= {25,30,35,40};

typedef char char4[4];

DEFINE_SOLE_MEMBER(char4);

*(SoleMember(char4)*)a1 = *(SoleMember(char4) const*)a2;
}


(The Standard guarantees that you can cast from a pointer to a struct, to a
pointer to the type of its first member. However, I don't believe it
guarantees the reverse -- so the code might be slightly dubious.)

In particular, it doesn't solve the alignment issue. The
alignment requirement for a struct object need not be a simple
function of the alignment requirements of its elements; the fact
that the enclosing object is a struct may impose alignment
requirements of its own. Therefore, either or both of the casts
in the above code may produce invalid struct pointers.

It also doesn't solve the size issue. The sizeof a struct
is at least as great as the sum of the sizeof its elements, but
may be greater. In the code above, sizeof(SoleMemberchar4) may
be greater than sizeof(char4), and if so the assignment tries to
read from memory beyond the end of the a2[] array, and undefined
behavior ensues.

One is left to ask: What issue *does* the above code solve?
Is it in any way better than the broken original, or is it just
a more sophisticated expression of the same errors?
 
F

Frederick Gotham

Eric Sosman posted:
One is left to ask: What issue *does* the above code solve?
Is it in any way better than the broken original, or is it just
a more sophisticated expression of the same errors?


The code makes the (naive?) assumption that a struct consisting of a sole
member is identical to the member on its own.

Is this guaranteed? No.

Is it reasonable? I don't know, but it sounds sort of logical.


<OFF-TOPIC>
You've just given me an idea for a post to comp.std.c++.
</OFF-TOPIC>
 
B

Ben Pfaff

Frederick Gotham said:
quantumred posted:
unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;

This will work perfectly if:

(1) 4 == sizeof(int)

(2) alignof(char[4]) >= alignof(int)

(3) unsigned int does not have trap representations, etc.
 
K

Keith Thompson

Frederick Gotham said:
quantumred posted:
unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;


This will work perfectly if:

(1) 4 == sizeof(int)

(2) alignof(char[4]) >= alignof(int)


The C Standard does not provide these guarantees for all platforms and
implementations.
[...]

Nor does it provide a function or operator called "alignof".

(That's not a complaint, just a clarification; your hypothetical
"alignof" operator is a good way to express the idea.)
 
S

SM Ryan

# I found the following code floating around somewhere and I'd like to
# get some comments.
#
# unsigned char a1[4]= { 5,10,15,20};
# unsigned char a2[4]= { 25,30,35,40};
#
# *(unsigned int *)a1=*(unsigned int *)a2;
#
# // now a1[0]=a2[0], a1[1]=a2[1], etc.
#
# This is taking a byte array, casting it to a 4 byte integer pointer,
# then dereferencing it, then assigning it. This results in the contents
# of array a1 copied to array a2. (corrections to my interpretation
# welcome)
#
# I don't know why this was done this way. My first instinct would have
# been to use memcpy. Is this faster than memcpy? Are there any dangers
# other than the potential for int not being 4 bytes?

If you have a 32 bit data path between memory and cpu, it's 4 times
faster to move an aligned word at a time instead of a byte at a time.
This can be a win on programs that do a lot of data movement on
aligned pointers.

A good implementation of memcpy will check lengths and alignments and
do the fastest possible move available on that particular machine.
Typical implementations are something else. Even a good implementation
without compiler assistance requires start up code everytime it is
called to determine what is the fastest move; often the compiler will
have all that information available, but it doesn't often happen that
information is made available to memcpy.

This kind of code is either defense against mediocre memcpy
implementations or a vain belief the programmer is cleverer
than the compiler.
 
J

Jack Klein

quantumred posted:
unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;


This will work perfectly if:

(1) 4 == sizeof(int)

(2) alignof(char[4]) >= alignof(int)


The C Standard does not provide these guarantees for all platforms and
implementations. What you have is non-portable, platform-specific code.

To be perfectly correct, the C standard does not provide these
guarantees for ANY platform OR implementation.
 
G

Giorgio Silvestri

Ben Pfaff said:
Frederick Gotham said:
quantumred posted:
unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;

This will work perfectly if:

(1) 4 == sizeof(int)

(2) alignof(char[4]) >= alignof(int)

(3) unsigned int does not have trap representations, etc.

I think It is not sufficient.

(3) unsigned int does not have padding bits

should be preferible.

In presence of padding bits

(*(unsigned int *)a2) can have many representations.

In this case with assignment

*(unsigned int *)a1=*(unsigned int *)a2;

you are not guaranteed to copy all bytes correctly.
 
D

Dave Thompson

quantumred posted:
unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;


This will work perfectly if:

(1) 4 == sizeof(int)

(2) alignof(char[4]) >= alignof(int)
To be pedantic, alignof (unsigned char [4]) % alignof (unsigned int)
== 0. Although it is clearly expected and normally true that stricter
alignments (if they exist at all) are multiples of all looser ones,
that isn't explicitly required; a perverse (DS9k) implementation could
align char[4] to 3 and int to 5.

To be accurate, alignof(a1) % alignof(int) == 0
and alignof(a2) % alignof(int) . An implementation reasonably could
(and I use one which does) align 'top-level' variables a1 and a2 more
strictly than u_char[4] requires, and in fact sufficient for u_int.
The C Standard does not provide these guarantees for all platforms and
implementations. What you have is non-portable, platform-specific code.

It seems to me that the author was aiming to optimise the copying of an
array. I'd probably take a route somewhat akin to the following:

#define DEFINE_SOLE_MEMBER(Type) \
\
struct SoleMember##Type { \
Type obj; \
} /* Usage code provides semi-colon */ \

#define SoleMember(Type) SoleMember##Type
Presumably for C you meant either SoleMember(Type) to expand to
struct SoleMember##Type

or the DEFINE_SOLE_MEMBER to do a typedef like
typedef struct { Type obj; } SoleMember##Type

int main()
{
unsigned char a1[4]= {5,10,15,20};
unsigned char a2[4]= {25,30,35,40};

typedef char char4[4];

DEFINE_SOLE_MEMBER(char4);

*(SoleMember(char4)*)a1 = *(SoleMember(char4) const*)a2;
}


(The Standard guarantees that you can cast from a pointer to a struct, to a
pointer to the type of its first member. However, I don't believe it
guarantees the reverse -- so the code might be slightly dubious.)

It does guarantee conversion from _actual_ (and hence correctly
aligned) first member back to the struct. But here of course the issue
is that _a variable of that same type_ may be misaligned. This is only
likely to be needed and happen in practice if the struct contains
other stricter members, but is formally allowed even if it doesn't.

- David.Thompson1 at worldnet.att.net
 
E

ena8t8si

Jack said:
quantumred posted:
unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;


This will work perfectly if:

(1) 4 == sizeof(int)

(2) alignof(char[4]) >= alignof(int)


The C Standard does not provide these guarantees for all platforms and
implementations. What you have is non-portable, platform-specific code.

To be perfectly correct, the C standard does not provide these
guarantees for ANY platform OR implementation.

And even if it did, the behaviour would still be
undefined.
 
K

Keith Thompson

Dave Thompson said:
quantumred posted:
unsigned char a1[4]= { 5,10,15,20};
unsigned char a2[4]= { 25,30,35,40};

*(unsigned int *)a1=*(unsigned int *)a2;


This will work perfectly if:

(1) 4 == sizeof(int)

(2) alignof(char[4]) >= alignof(int)
To be pedantic, alignof (unsigned char [4]) % alignof (unsigned int)
== 0. Although it is clearly expected and normally true that stricter
alignments (if they exist at all) are multiples of all looser ones,
that isn't explicitly required; a perverse (DS9k) implementation could
align char[4] to 3 and int to 5.
[...]

Actually, I don't think it could. int could have an alignment of 5,
but char[4] can't have a required alignment of 3. Given:

char arr[2][4]

the two elements of arr must be adjacent and must each have a size of
4 bytes.

In general, because there can't be gaps between array elements, the
size of a type must be a multiple of its alignment. (The
implementation can adjust the size if necessary to make this happen,
for example by adding padding bits.)
 
D

Dave Thompson

To be pedantic, alignof (unsigned char [4]) % alignof (unsigned int)
== 0. Although it is clearly expected and normally true that stricter
alignments (if they exist at all) are multiples of all looser ones,
that isn't explicitly required; a perverse (DS9k) implementation could
align char[4] to 3 and int to 5.
[...]

Actually, I don't think it could. int could have an alignment of 5,
but char[4] can't have a required alignment of 3. Given:

char arr[2][4]

the two elements of arr must be adjacent and must each have a size of
4 bytes.
Oops, you're right. I didn't think far enough.
In general, because there can't be gaps between array elements, the
size of a type must be a multiple of its alignment. (The
implementation can adjust the size if necessary to make this happen,
for example by adding padding bits.)

Except to character types, or at least unsigned char and thus possibly
plain char. And, by the reasoning above, arrays of them.

But I still maintain in the example upthread int could (perversely)
require an alignment not supplied by char[4].

- David.Thompson1 at worldnet.att.net
 
E

ena8t8si

Dave said:
To be pedantic, alignof (unsigned char [4]) % alignof (unsigned int)
== 0. Although it is clearly expected and normally true that stricter
alignments (if they exist at all) are multiples of all looser ones,
that isn't explicitly required; a perverse (DS9k) implementation could
align char[4] to 3 and int to 5.
[...]

Actually, I don't think it could. int could have an alignment of 5,
but char[4] can't have a required alignment of 3. Given:

char arr[2][4]

the two elements of arr must be adjacent and must each have a size of
4 bytes.
Oops, you're right. I didn't think far enough.
In general, because there can't be gaps between array elements, the
size of a type must be a multiple of its alignment. (The
implementation can adjust the size if necessary to make this happen,
for example by adding padding bits.)

Except to character types, or at least unsigned char and thus possibly
plain char. And, by the reasoning above, arrays of them.

But I still maintain in the example upthread int could (perversely)
require an alignment not supplied by char[4].

Are you saying anything more than, eg, char[4] can
require four byte alignment whereas int could allow
alignment of either 0 or 2 mod 4? Certainly the
set of alignments allowed for int must have some
overlap with the set of alignments allowed for
char[4].
 
K

Keith Thompson

Dave said:
To be pedantic, alignof (unsigned char [4]) % alignof (unsigned int)
== 0. Although it is clearly expected and normally true that stricter
alignments (if they exist at all) are multiples of all looser ones,
that isn't explicitly required; a perverse (DS9k) implementation could
align char[4] to 3 and int to 5.
[...]

Actually, I don't think it could. int could have an alignment of 5,
but char[4] can't have a required alignment of 3. Given:

char arr[2][4]

the two elements of arr must be adjacent and must each have a size of
4 bytes.
Oops, you're right. I didn't think far enough.
In general, because there can't be gaps between array elements, the
size of a type must be a multiple of its alignment. (The
implementation can adjust the size if necessary to make this happen,
for example by adding padding bits.)

Except to character types, or at least unsigned char and thus possibly
plain char. And, by the reasoning above, arrays of them.

But I still maintain in the example upthread int could (perversely)
require an alignment not supplied by char[4].

Are you saying anything more than, eg, char[4] can
require four byte alignment whereas int could allow
alignment of either 0 or 2 mod 4? Certainly the
set of alignments allowed for int must have some
overlap with the set of alignments allowed for
char[4].

int could have an alignment of, say, 5. If char[4] required an
alignment of 4, then a union of an int and a char[4] would require an
alignment of at least 20.

But I'm not sure that char[4] can require an alignment stricter than 1
byte. For example, this:

char buf[5];
char (*p)[4] = (char(*)[4])(buf+1);

make p a pointer to a char[4] array that aliases elements 1..4 of buf.
Is this guaranteed to make p a valid pointer?

More generally, may an array of type X have a stricter alignment
requirement than type X itself?

An implementation may, of course, choose to align a char array, or
even a single char object, on, say, a word boundary; the question is
whether it can require such strict alignment.
 
W

Walter Roberson

Keith Thompson said:
More generally, may an array of type X have a stricter alignment
requirement than type X itself?

No: that would interfere with the requirement that the address of
a variable of non-aggregate type is equivilent to the address
of an array of length 1 of that type. (If I recall correctly, this
requirement is part of the definition of pointer comparison.)
 
E

ena8t8si

Walter said:
No: that would interfere with the requirement that the address of
a variable of non-aggregate type is equivilent to the address
of an array of length 1 of that type. (If I recall correctly, this
requirement is part of the definition of pointer comparison.)

Keith was talking about arrays of length greater than one.
 

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

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,817
Latest member
AdalbertoT

Latest Threads

Top