Dereferencing type-punned pointer

J

John May

I keep getting this warning in GCC with the code segment below.

file.c:256 "warning: dereferencing type-punned pointer will break
strict-aliasing rules"

/*****************************************/
char buffer[SIZE];
int length, type;

/* Code to fill buffer */

length = ntohs( *((unsigned short *)buffer) );
type = ntohs( *((unsigned short *)buffer + 1) );

/*****************************************/


I'm trying to take the first two bytes (in network byte order) off the
buffer and put them in the length, followed by the next two bytes into
the type. GCC complains about the length conversion, but not the type.

What does this warning mean, and should I bother with it?

I don't want to keep checking this forum, so answers by email required.
Quick responses appreciated.

John
 
K

Keith Thompson

John May said:
I keep getting this warning in GCC with the code segment below.

file.c:256 "warning: dereferencing type-punned pointer will break
strict-aliasing rules"

/*****************************************/
char buffer[SIZE];
int length, type;

/* Code to fill buffer */

length = ntohs( *((unsigned short *)buffer) );
type = ntohs( *((unsigned short *)buffer + 1) );

/*****************************************/


I'm trying to take the first two bytes (in network byte order) off the
buffer and put them in the length, followed by the next two bytes into
the type. GCC complains about the length conversion, but not the type.

What does this warning mean, and should I bother with it?

Assuming buffer is aligned at an even address, buffer + 1 is at an odd
address; attempting to read a short object from an odd address can
easily crash your program. (x86 systems, I think, don't impose strict
alignment requirements.)

If you want to copy bytes from an array of char into a larger integer,
either do so explicitly or use memcpy().

You also appear to be making some assumptions about the sizes of short
and int. You should at least document those assumptions.
I don't want to keep checking this forum, so answers by email required.
Quick responses appreciated.

Answers posted here are not just for your benefit; they're for everyone
who reads this newsgroup. Feel free to explain why you should get
special treatment, but don't expect to convince anyone. Post here, get
your answers here.
 
K

Keith Thompson

John May said:
I don't want to keep checking this forum, so answers by email required.
Quick responses appreciated.

Unless <[email protected]> is your real e-mail address, how do you
expect to get answers by e-mail?

And if it isn't, you shouldn't use a real domain name as a fake address
(yes, there really is a nospam.com); it can result in the owners of that
domain receiving spam meant for you. "example.com" is guaranteed not to
exist.
 
B

Ben Bacarisse

Keith Thompson said:
John May <[email protected]> writes:
char buffer[SIZE];
int length, type;

/* Code to fill buffer */

length = ntohs( *((unsigned short *)buffer) );
type = ntohs( *((unsigned short *)buffer + 1) );
I'm trying to take the first two bytes (in network byte order) off the
buffer and put them in the length, followed by the next two bytes into
the type. GCC complains about the length conversion, but not the type.

What does this warning mean, and should I bother with it?

Assuming buffer is aligned at an even address, buffer + 1 is at an odd
address; attempting to read a short object from an odd address can
easily crash your program.

The cast has higher precedence so unless the size of short is odd, the
second access uses an address with the same parity as the first.

I've phrased it like this because there's nothing actually wrong with
what you said -- it just isn't relevant to the code in question!

<snip>
 
B

Ben Bacarisse

John May said:
I keep getting this warning in GCC with the code segment below.

file.c:256 "warning: dereferencing type-punned pointer will break
strict-aliasing rules"

/*****************************************/
char buffer[SIZE];
int length, type;

/* Code to fill buffer */

length = ntohs( *((unsigned short *)buffer) );
type = ntohs( *((unsigned short *)buffer + 1) );

/*****************************************/


I'm trying to take the first two bytes (in network byte order) off the
buffer and put them in the length, followed by the next two bytes into
the type. GCC complains about the length conversion, but not the type.

What does this warning mean, and should I bother with it?

gcc assumes (as it is permitted to do) that an object is only accessed
via an expression of "the right type" -- usually just the declared type
of the object. The objects in 'buffer' are declared to be chars, but
the pointer cast and dereference causes them to be accessed using an
expression of type short. Because the breaks gcc's assumptions it warns
you. It's generally a bad way to do this kind of thing.

(The phrase "the right type" is in quotes because the actual rules are
rather complex so my explanation is very much a simplification.)

An alternative is to use a union:

union {
uint8_t bytes[SIZE];
uint16_t words[SIZE/2];
} buf_type;

but_type buffer;
...
length = ntohs(buffer.words[0]);
type = ntohs(buffer.words[1]);

(The uintN_t types are standard C types from stdint.h.)

Alternatively you can construct the two values from the individual
bytes:

length = ((unsigned)buffer[0] << 8) + buffer[1];

but this is fiddly -- you should make the buffer an array of unsigned
char and you still need the cast before the shift for the code to be
properly portable.
I don't want to keep checking this forum, so answers by email required.
Quick responses appreciated.

Sorry -- impossible for reasons already explained.
 
I

Ike Naar

Unless <[email protected]> is your real e-mail address, how do you
expect to get answers by e-mail?

And if it isn't, you shouldn't use a real domain name as a fake address
(yes, there really is a nospam.com); it can result in the owners of that
domain receiving spam meant for you. "example.com" is guaranteed not to
exist.

Are you sure about that? "example" does not exist as a top-level
domain, but is it also a reserved name for subdomains? (I thought
not).
Perhaps better choices would be "nospam.example" or "nospam.invalid".
 
N

Nobody

length = ntohs(buffer.words[0]);
type = ntohs(buffer.words[1]);

(The uintN_t types are standard C types from stdint.h.)

Also, the type of ntohs() as specified by POSIX is:

uint16_t ntohs(uint16_t);
 

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,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top