'Fix for dereferencing type-punned pointer will break strict-aliasing
I'm trying to fix two warnings when compiling a specific program using GCC. The warnings are:
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
and the two culprits are:
unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));
and
*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
incoming_buf and outgoing_buf are defined as follows:
char incoming_buf[LIBIRC_DCC_BUFFER_SIZE];
char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];
This seems subtly different than the other examples of that warning I've been examining. I would prefer to fix the problem rather than disable strict-aliasing checks.
There have been many suggestions to use a union - what might be a suitable union for this case?
Solution 1:[1]
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).
Solution 2:[2]
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
Solution 3:[3]
If I may, IMHO, for this case, the problem is the design of the ntohl and htonl and related function APIs. They should not have been written as numeric argument with numeric return. (and yes, I understand the macro optimization point) They should have been designed as the 'n' side being a pointer to a buffer. When this is done, the whole problem goes away and the routine is accurate whichever endian the host is. For example (with no attempt to optimize):
inline void safe_htonl(unsigned char *netside, unsigned long value) {
netside[3] = value & 0xFF;
netside[2] = (value >> 8) & 0xFF;
netside[1] = (value >> 16) & 0xFF;
netside[0] = (value >> 24) & 0xFF;
};
Solution 4:[4]
If you have reasons that do not allow you to change type of source object (like it was in my case), and you are absolutely confident that the code is correct and it does what intended to do with that char array, to avoid warnings you may do the following:
unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);
Solution 5:[5]
I recently upgraded a project from GCC 6 to GCC 9, and started seeing this warning. The project is on a 32-bit microcontroller, and I had created a struct to access the individual bytes of a 32-bit machine register:
struct TCC_WEXCTRL_t
{
byte OTMX;
byte DTIEN;
byte DTLS;
byte DTHS;
};
and then coded:
((TCC_WEXCTRL_t *)&TCC0->WEXCTRL)->DTLS = PwmLoDeadTime;
which produced the warning in the new compiler. I found I could eliminate the warning by combining my struct in a union with the original type:
union TCC_WEXCTRL_t
{
TCC_WEXCTRL_Type std;
struct
{
byte OTMX;
byte DTIEN;
byte DTLS;
byte DTHS;
};
};
where TCC_WEXCTRL_Type is the type of the WEXCTRL member as provided in the manufacturer's header files.
I'm not sure if this is considered a fully compliant fix, or if GCC is just failing to catch it. If this hadn't worked (or gets caught in another GCC upgrade), I would move on to using a union of the pointer types, as described on this thread by Real Name.
Solution 6:[6]
C cast didn't work, but reinterpret_cast<> helped me in similar situation.
Solution 7:[7]
If you are certain you know what you are doing, do this:
void *tmp = dcc->incoming_buf;
unsigned int received_size = ntohl (*((unsigned int*) tmp));
or just:
unsigned int received_size = ntohl (*((unsigned int*) ((void *) dcc->incoming_buf)));
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Kerrek SB |
| Solution 2 | |
| Solution 3 | |
| Solution 4 | Oleg Oleg |
| Solution 5 | DosMan |
| Solution 6 | Evgeny Yashin |
| Solution 7 |
