Fix for dereferencing type-punned pointer will break strict-aliasing
union
{
const unsigned int * int_val_p;
const char* buf;
} xyz;
xyz.buf = dcc->incoming_buf;
unsigned int received_size = ntohl(*(xyz.int_val_p));
Simplified explanation 1. c++ standard states that you should attempt to align data yourself, g++ goes an extra mile to generate warnings on the subject. 2. you should only attempt it if you completely understand the data alignment on your architecture/system and inside your code (for example the code above is a sure thing on Intel 32/64 ; alignment 1; Win/Linux/Bsd/Mac) 3. the only practical reason to use the code above is to avoid compiler warnings , WHEN and IF you know what you are doing
To fix the problem, don't pun and alias! The only "correct" way to read a type T
is to allocate a type T
and populate its representation if needed:
uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);
In short: If you want an integer, you need to make an integer. There's no way to cheat around that in a language-condoned way.
The only pointer conversion which you are allowed (for purposes of I/O, generally) is to treat the address of an existing variable of type T
as a char*
, or rather, as the pointer to the first element of an array of chars of size sizeof(T)
.
First off, let's examine why you get the aliasing violation warnings.
Aliasing rules simply say that you can only access an object through its own type, its signed / unsigned variant type, or through a character type (char
, signed char
, unsigned char
).
C says violating aliasing rules invokes undefined behavior (so don't!).
In this line of your program:
unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));
although the elements of the incoming_buf
array are of type char
, you are accessing them as unsigned int
. Indeed the result of the dereference operator in the expression *((unsigned int*)dcc->incoming_buf)
is of unsigned int
type.
This is a violation of the aliasing rules, because you only have the right to access elements of incoming_buf
array through (see rules summary above!) char
, signed char
or unsigned char
.
Notice you have exactly the same aliasing issue in your second culprit:
*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
You access the char
elements of outgoing_buf
through unsigned int
, so it's an aliasing violation.
Proposed solution
To fix your issue, you could try to have the elements of your arrays directly defined in the type you want to access:
unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
(By the way the width of unsigned int
is implementation defined, so you should consider using uint32_t
if your program assumes unsigned int
is 32-bit).
This way you could store unsigned int
objects in your array without violating the aliasing rules by accessing the element through the type char
, like this:
*((char *) outgoing_buf) = expr_of_type_char;
or
char_lvalue = *((char *) incoming_buf);
EDIT:
I've entirely reworked my answer, in particular I explain why the program gets the aliasing warnings from the compiler.