Strict aliasing rule uint8_t buffer to structure
Consider I have a typedef with bit fields as below.
typedef struct VIN_oCAN01_3abd61be
{
uint64_t var1:24;
uint64_t var2:4;
uint64_t var3:4
}__attribute__((packed))Message;
And I receive uint8_t
buffer as below.
uint8_t buffer[4] = {0x1,0x2,0x3,0x14};
Currently in my production program my team is suggesting below approach.
Message *ptrMsg = (Message *)buffer;
That is, assigning uint8_t
buffer to Message
type pointer.
I had suggested that proposed approach does not follow strict aliasing rule and instead we should do as below.
Message msg;
memcpy(&msg,buffer, sizeof(msg));
Note there is no option of copying the buffer to structure
manually(member by member) as structure is very big.
Is my understanding is correct? If so can you please provide the standard doc which I can use to prove my point.
c
|
show 1 more comment
Consider I have a typedef with bit fields as below.
typedef struct VIN_oCAN01_3abd61be
{
uint64_t var1:24;
uint64_t var2:4;
uint64_t var3:4
}__attribute__((packed))Message;
And I receive uint8_t
buffer as below.
uint8_t buffer[4] = {0x1,0x2,0x3,0x14};
Currently in my production program my team is suggesting below approach.
Message *ptrMsg = (Message *)buffer;
That is, assigning uint8_t
buffer to Message
type pointer.
I had suggested that proposed approach does not follow strict aliasing rule and instead we should do as below.
Message msg;
memcpy(&msg,buffer, sizeof(msg));
Note there is no option of copying the buffer to structure
manually(member by member) as structure is very big.
Is my understanding is correct? If so can you please provide the standard doc which I can use to prove my point.
c
Besides the strict aliasing violation problem, beware of indianness. Filling a uint from a char with memcpy makes your code completely non portable.
– Alain Merigot
Jan 17 at 13:48
@AlainMerigot Good catch!, We are handling endianness just before thememcpy
or pointer assignment, it is just that I have not shown here.
– kiran Biradar
Jan 17 at 13:53
I think that strict aliasing is a compiler thing and usually it has a flag if it may or may not assume strict aliasing for different optimization levels.
– Alex Lop.
Jan 17 at 13:58
1
@user694733 I have bit fields defined.
– kiran Biradar
Jan 17 at 14:02
1
Your bitfields add up to 32 bits, in auint64_t
. You have no idea where in those 64 bits those bitfields actually are. Per 6.7.2.1p11: "whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified." Bitfields are just plain bad news for portability and maintainability.
– Andrew Henle
Jan 17 at 14:21
|
show 1 more comment
Consider I have a typedef with bit fields as below.
typedef struct VIN_oCAN01_3abd61be
{
uint64_t var1:24;
uint64_t var2:4;
uint64_t var3:4
}__attribute__((packed))Message;
And I receive uint8_t
buffer as below.
uint8_t buffer[4] = {0x1,0x2,0x3,0x14};
Currently in my production program my team is suggesting below approach.
Message *ptrMsg = (Message *)buffer;
That is, assigning uint8_t
buffer to Message
type pointer.
I had suggested that proposed approach does not follow strict aliasing rule and instead we should do as below.
Message msg;
memcpy(&msg,buffer, sizeof(msg));
Note there is no option of copying the buffer to structure
manually(member by member) as structure is very big.
Is my understanding is correct? If so can you please provide the standard doc which I can use to prove my point.
c
Consider I have a typedef with bit fields as below.
typedef struct VIN_oCAN01_3abd61be
{
uint64_t var1:24;
uint64_t var2:4;
uint64_t var3:4
}__attribute__((packed))Message;
And I receive uint8_t
buffer as below.
uint8_t buffer[4] = {0x1,0x2,0x3,0x14};
Currently in my production program my team is suggesting below approach.
Message *ptrMsg = (Message *)buffer;
That is, assigning uint8_t
buffer to Message
type pointer.
I had suggested that proposed approach does not follow strict aliasing rule and instead we should do as below.
Message msg;
memcpy(&msg,buffer, sizeof(msg));
Note there is no option of copying the buffer to structure
manually(member by member) as structure is very big.
Is my understanding is correct? If so can you please provide the standard doc which I can use to prove my point.
c
c
asked Jan 17 at 13:29
kiran Biradarkiran Biradar
5,0712926
5,0712926
Besides the strict aliasing violation problem, beware of indianness. Filling a uint from a char with memcpy makes your code completely non portable.
– Alain Merigot
Jan 17 at 13:48
@AlainMerigot Good catch!, We are handling endianness just before thememcpy
or pointer assignment, it is just that I have not shown here.
– kiran Biradar
Jan 17 at 13:53
I think that strict aliasing is a compiler thing and usually it has a flag if it may or may not assume strict aliasing for different optimization levels.
– Alex Lop.
Jan 17 at 13:58
1
@user694733 I have bit fields defined.
– kiran Biradar
Jan 17 at 14:02
1
Your bitfields add up to 32 bits, in auint64_t
. You have no idea where in those 64 bits those bitfields actually are. Per 6.7.2.1p11: "whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified." Bitfields are just plain bad news for portability and maintainability.
– Andrew Henle
Jan 17 at 14:21
|
show 1 more comment
Besides the strict aliasing violation problem, beware of indianness. Filling a uint from a char with memcpy makes your code completely non portable.
– Alain Merigot
Jan 17 at 13:48
@AlainMerigot Good catch!, We are handling endianness just before thememcpy
or pointer assignment, it is just that I have not shown here.
– kiran Biradar
Jan 17 at 13:53
I think that strict aliasing is a compiler thing and usually it has a flag if it may or may not assume strict aliasing for different optimization levels.
– Alex Lop.
Jan 17 at 13:58
1
@user694733 I have bit fields defined.
– kiran Biradar
Jan 17 at 14:02
1
Your bitfields add up to 32 bits, in auint64_t
. You have no idea where in those 64 bits those bitfields actually are. Per 6.7.2.1p11: "whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified." Bitfields are just plain bad news for portability and maintainability.
– Andrew Henle
Jan 17 at 14:21
Besides the strict aliasing violation problem, beware of indianness. Filling a uint from a char with memcpy makes your code completely non portable.
– Alain Merigot
Jan 17 at 13:48
Besides the strict aliasing violation problem, beware of indianness. Filling a uint from a char with memcpy makes your code completely non portable.
– Alain Merigot
Jan 17 at 13:48
@AlainMerigot Good catch!, We are handling endianness just before the
memcpy
or pointer assignment, it is just that I have not shown here.– kiran Biradar
Jan 17 at 13:53
@AlainMerigot Good catch!, We are handling endianness just before the
memcpy
or pointer assignment, it is just that I have not shown here.– kiran Biradar
Jan 17 at 13:53
I think that strict aliasing is a compiler thing and usually it has a flag if it may or may not assume strict aliasing for different optimization levels.
– Alex Lop.
Jan 17 at 13:58
I think that strict aliasing is a compiler thing and usually it has a flag if it may or may not assume strict aliasing for different optimization levels.
– Alex Lop.
Jan 17 at 13:58
1
1
@user694733 I have bit fields defined.
– kiran Biradar
Jan 17 at 14:02
@user694733 I have bit fields defined.
– kiran Biradar
Jan 17 at 14:02
1
1
Your bitfields add up to 32 bits, in a
uint64_t
. You have no idea where in those 64 bits those bitfields actually are. Per 6.7.2.1p11: "whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified." Bitfields are just plain bad news for portability and maintainability.– Andrew Henle
Jan 17 at 14:21
Your bitfields add up to 32 bits, in a
uint64_t
. You have no idea where in those 64 bits those bitfields actually are. Per 6.7.2.1p11: "whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified." Bitfields are just plain bad news for portability and maintainability.– Andrew Henle
Jan 17 at 14:21
|
show 1 more comment
2 Answers
2
active
oldest
votes
I had suggested that proposed approach does not follow strict aliasing rule
Correct. ptrMsg = (Message *)buffer
means that you cannot access the data of ptrMsg
without invoking undefined behavior.
You can prove your point with C17 6.5 §7 (cited here - What is the strict aliasing rule?). An lvalue expression such as ptrMsg->var1 = value
does not access the stored value through a type compatible with the effective type of what's stored there, nor through any of the allowed expressions.
You can however go from Message
to an array of uint8_t
(assuming uint8_t
is a character type) without violating strict aliasing.
The larger problem is however the precece of the bit-field in the first place, which is non-standard and non-portable. For example, you cannot know which part of the bit-field that is the MSB and LSB. You cannot know how the bits are aligned in the 64 bit type. Using a 64 bit type for a bit-field is a non-standard extension. It is endianess-dependent. And so on.
Assuming the 24 bits refer to bit 31 to 8 (we can't know by reading your code), then proper code without strict aliasing violations, bit-field madness and non-standard "struct padding killers" would look like this:
typedef union
{
uint32_t var;
uint8_t bytes[4];
} Message;
uint8_t buffer[4];
Message* ptrMsg = (Message*)buffer;
uint32_t var1 = (ptrMsg->var >> 8);
uint8_t var2 = (ptrMsg->var >> 4) & 0x0F;
uint8_t var3 = (ptrMsg->var) & 0x0F;
Message
being "a union type that includes one of the aforementioned types among its
members". Meaning it contains a type compatible with uint8_t [4]
.
This code also contains no copying and the shifts will get translated to the relevant bit access in the machine code.
add a comment |
You are right.
C17 draft § 6.5:
An object shall have its stored value accessed only by an lvalue expression that has one of the
following types: 89)
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective
type of the object,
- an aggregate or union type that includes one of the aforementioned types among its members
(including, recursively, a member of a subaggregate or contained union), or
- a character type.
In this case the object is of type uint8_t
and type of the lvalue expression is Message
. None of the exceptions above apply.
Using memcpy
over dereferences fixes this issue, or alternatively you can disable the strict aliasing in your compiler if you want to write in non-C language.
But even after this the code has a lot of problems and is not portable: Bitfields are extremely poorly defined in standard and overall structures are very clumsy way to handle serialization. You should take the option of deserializing each member manually.
1
In all fairness, disabling strict aliasing is not a bad idea at all. In particularly or embedded systems design, which this appears to be. Strict aliasing/effective type is a broken part of the C language, specified by PC programmers who don't grasp the type punning often needed in embedded systems. In addition, GCC is the only compiler I know of which abuses strict aliasing to produce faster code that does not at all perform what the unaware programmer intended. But it does so efficiently! Better to always usegcc -fno-strict-aliasing
.
– Lundin
Jan 17 at 14:22
1
@Lundin But the flip side of that is programmers with no experience on systems where violating strict aliasing causes actual problems. Thus you get questions like this one, where a bunch of programmers with apparently no understanding at all regarding strict aliasing looks to have prompted someone who does understand the implications to post a question on Stackoverflow.
– Andrew Henle
Jan 17 at 14:28
@AndrewHenle But the strict aliasing rule isn't really common knowledge. It takes a hardened C veteran/language nerd to know about it and understand it. Which in turn makes it a safety hazard and language flaw. Particularly since prior C99, no compiler did aggressive optimizations relying on it... and it took around 10 years for C99 to become industry standard. We didn't really see strict aliasing bugs in embedded systems until around 2009, when gcc started to become popular as ARM compiler. And so the non-nerds have to use MISRA-C to tell them "don't do this" without understanding why.
– Lundin
Jan 17 at 15:35
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54237004%2fstrict-aliasing-rule-uint8-t-buffer-to-structure%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
I had suggested that proposed approach does not follow strict aliasing rule
Correct. ptrMsg = (Message *)buffer
means that you cannot access the data of ptrMsg
without invoking undefined behavior.
You can prove your point with C17 6.5 §7 (cited here - What is the strict aliasing rule?). An lvalue expression such as ptrMsg->var1 = value
does not access the stored value through a type compatible with the effective type of what's stored there, nor through any of the allowed expressions.
You can however go from Message
to an array of uint8_t
(assuming uint8_t
is a character type) without violating strict aliasing.
The larger problem is however the precece of the bit-field in the first place, which is non-standard and non-portable. For example, you cannot know which part of the bit-field that is the MSB and LSB. You cannot know how the bits are aligned in the 64 bit type. Using a 64 bit type for a bit-field is a non-standard extension. It is endianess-dependent. And so on.
Assuming the 24 bits refer to bit 31 to 8 (we can't know by reading your code), then proper code without strict aliasing violations, bit-field madness and non-standard "struct padding killers" would look like this:
typedef union
{
uint32_t var;
uint8_t bytes[4];
} Message;
uint8_t buffer[4];
Message* ptrMsg = (Message*)buffer;
uint32_t var1 = (ptrMsg->var >> 8);
uint8_t var2 = (ptrMsg->var >> 4) & 0x0F;
uint8_t var3 = (ptrMsg->var) & 0x0F;
Message
being "a union type that includes one of the aforementioned types among its
members". Meaning it contains a type compatible with uint8_t [4]
.
This code also contains no copying and the shifts will get translated to the relevant bit access in the machine code.
add a comment |
I had suggested that proposed approach does not follow strict aliasing rule
Correct. ptrMsg = (Message *)buffer
means that you cannot access the data of ptrMsg
without invoking undefined behavior.
You can prove your point with C17 6.5 §7 (cited here - What is the strict aliasing rule?). An lvalue expression such as ptrMsg->var1 = value
does not access the stored value through a type compatible with the effective type of what's stored there, nor through any of the allowed expressions.
You can however go from Message
to an array of uint8_t
(assuming uint8_t
is a character type) without violating strict aliasing.
The larger problem is however the precece of the bit-field in the first place, which is non-standard and non-portable. For example, you cannot know which part of the bit-field that is the MSB and LSB. You cannot know how the bits are aligned in the 64 bit type. Using a 64 bit type for a bit-field is a non-standard extension. It is endianess-dependent. And so on.
Assuming the 24 bits refer to bit 31 to 8 (we can't know by reading your code), then proper code without strict aliasing violations, bit-field madness and non-standard "struct padding killers" would look like this:
typedef union
{
uint32_t var;
uint8_t bytes[4];
} Message;
uint8_t buffer[4];
Message* ptrMsg = (Message*)buffer;
uint32_t var1 = (ptrMsg->var >> 8);
uint8_t var2 = (ptrMsg->var >> 4) & 0x0F;
uint8_t var3 = (ptrMsg->var) & 0x0F;
Message
being "a union type that includes one of the aforementioned types among its
members". Meaning it contains a type compatible with uint8_t [4]
.
This code also contains no copying and the shifts will get translated to the relevant bit access in the machine code.
add a comment |
I had suggested that proposed approach does not follow strict aliasing rule
Correct. ptrMsg = (Message *)buffer
means that you cannot access the data of ptrMsg
without invoking undefined behavior.
You can prove your point with C17 6.5 §7 (cited here - What is the strict aliasing rule?). An lvalue expression such as ptrMsg->var1 = value
does not access the stored value through a type compatible with the effective type of what's stored there, nor through any of the allowed expressions.
You can however go from Message
to an array of uint8_t
(assuming uint8_t
is a character type) without violating strict aliasing.
The larger problem is however the precece of the bit-field in the first place, which is non-standard and non-portable. For example, you cannot know which part of the bit-field that is the MSB and LSB. You cannot know how the bits are aligned in the 64 bit type. Using a 64 bit type for a bit-field is a non-standard extension. It is endianess-dependent. And so on.
Assuming the 24 bits refer to bit 31 to 8 (we can't know by reading your code), then proper code without strict aliasing violations, bit-field madness and non-standard "struct padding killers" would look like this:
typedef union
{
uint32_t var;
uint8_t bytes[4];
} Message;
uint8_t buffer[4];
Message* ptrMsg = (Message*)buffer;
uint32_t var1 = (ptrMsg->var >> 8);
uint8_t var2 = (ptrMsg->var >> 4) & 0x0F;
uint8_t var3 = (ptrMsg->var) & 0x0F;
Message
being "a union type that includes one of the aforementioned types among its
members". Meaning it contains a type compatible with uint8_t [4]
.
This code also contains no copying and the shifts will get translated to the relevant bit access in the machine code.
I had suggested that proposed approach does not follow strict aliasing rule
Correct. ptrMsg = (Message *)buffer
means that you cannot access the data of ptrMsg
without invoking undefined behavior.
You can prove your point with C17 6.5 §7 (cited here - What is the strict aliasing rule?). An lvalue expression such as ptrMsg->var1 = value
does not access the stored value through a type compatible with the effective type of what's stored there, nor through any of the allowed expressions.
You can however go from Message
to an array of uint8_t
(assuming uint8_t
is a character type) without violating strict aliasing.
The larger problem is however the precece of the bit-field in the first place, which is non-standard and non-portable. For example, you cannot know which part of the bit-field that is the MSB and LSB. You cannot know how the bits are aligned in the 64 bit type. Using a 64 bit type for a bit-field is a non-standard extension. It is endianess-dependent. And so on.
Assuming the 24 bits refer to bit 31 to 8 (we can't know by reading your code), then proper code without strict aliasing violations, bit-field madness and non-standard "struct padding killers" would look like this:
typedef union
{
uint32_t var;
uint8_t bytes[4];
} Message;
uint8_t buffer[4];
Message* ptrMsg = (Message*)buffer;
uint32_t var1 = (ptrMsg->var >> 8);
uint8_t var2 = (ptrMsg->var >> 4) & 0x0F;
uint8_t var3 = (ptrMsg->var) & 0x0F;
Message
being "a union type that includes one of the aforementioned types among its
members". Meaning it contains a type compatible with uint8_t [4]
.
This code also contains no copying and the shifts will get translated to the relevant bit access in the machine code.
edited Jan 17 at 16:30
answered Jan 17 at 14:18
LundinLundin
108k17159262
108k17159262
add a comment |
add a comment |
You are right.
C17 draft § 6.5:
An object shall have its stored value accessed only by an lvalue expression that has one of the
following types: 89)
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective
type of the object,
- an aggregate or union type that includes one of the aforementioned types among its members
(including, recursively, a member of a subaggregate or contained union), or
- a character type.
In this case the object is of type uint8_t
and type of the lvalue expression is Message
. None of the exceptions above apply.
Using memcpy
over dereferences fixes this issue, or alternatively you can disable the strict aliasing in your compiler if you want to write in non-C language.
But even after this the code has a lot of problems and is not portable: Bitfields are extremely poorly defined in standard and overall structures are very clumsy way to handle serialization. You should take the option of deserializing each member manually.
1
In all fairness, disabling strict aliasing is not a bad idea at all. In particularly or embedded systems design, which this appears to be. Strict aliasing/effective type is a broken part of the C language, specified by PC programmers who don't grasp the type punning often needed in embedded systems. In addition, GCC is the only compiler I know of which abuses strict aliasing to produce faster code that does not at all perform what the unaware programmer intended. But it does so efficiently! Better to always usegcc -fno-strict-aliasing
.
– Lundin
Jan 17 at 14:22
1
@Lundin But the flip side of that is programmers with no experience on systems where violating strict aliasing causes actual problems. Thus you get questions like this one, where a bunch of programmers with apparently no understanding at all regarding strict aliasing looks to have prompted someone who does understand the implications to post a question on Stackoverflow.
– Andrew Henle
Jan 17 at 14:28
@AndrewHenle But the strict aliasing rule isn't really common knowledge. It takes a hardened C veteran/language nerd to know about it and understand it. Which in turn makes it a safety hazard and language flaw. Particularly since prior C99, no compiler did aggressive optimizations relying on it... and it took around 10 years for C99 to become industry standard. We didn't really see strict aliasing bugs in embedded systems until around 2009, when gcc started to become popular as ARM compiler. And so the non-nerds have to use MISRA-C to tell them "don't do this" without understanding why.
– Lundin
Jan 17 at 15:35
add a comment |
You are right.
C17 draft § 6.5:
An object shall have its stored value accessed only by an lvalue expression that has one of the
following types: 89)
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective
type of the object,
- an aggregate or union type that includes one of the aforementioned types among its members
(including, recursively, a member of a subaggregate or contained union), or
- a character type.
In this case the object is of type uint8_t
and type of the lvalue expression is Message
. None of the exceptions above apply.
Using memcpy
over dereferences fixes this issue, or alternatively you can disable the strict aliasing in your compiler if you want to write in non-C language.
But even after this the code has a lot of problems and is not portable: Bitfields are extremely poorly defined in standard and overall structures are very clumsy way to handle serialization. You should take the option of deserializing each member manually.
1
In all fairness, disabling strict aliasing is not a bad idea at all. In particularly or embedded systems design, which this appears to be. Strict aliasing/effective type is a broken part of the C language, specified by PC programmers who don't grasp the type punning often needed in embedded systems. In addition, GCC is the only compiler I know of which abuses strict aliasing to produce faster code that does not at all perform what the unaware programmer intended. But it does so efficiently! Better to always usegcc -fno-strict-aliasing
.
– Lundin
Jan 17 at 14:22
1
@Lundin But the flip side of that is programmers with no experience on systems where violating strict aliasing causes actual problems. Thus you get questions like this one, where a bunch of programmers with apparently no understanding at all regarding strict aliasing looks to have prompted someone who does understand the implications to post a question on Stackoverflow.
– Andrew Henle
Jan 17 at 14:28
@AndrewHenle But the strict aliasing rule isn't really common knowledge. It takes a hardened C veteran/language nerd to know about it and understand it. Which in turn makes it a safety hazard and language flaw. Particularly since prior C99, no compiler did aggressive optimizations relying on it... and it took around 10 years for C99 to become industry standard. We didn't really see strict aliasing bugs in embedded systems until around 2009, when gcc started to become popular as ARM compiler. And so the non-nerds have to use MISRA-C to tell them "don't do this" without understanding why.
– Lundin
Jan 17 at 15:35
add a comment |
You are right.
C17 draft § 6.5:
An object shall have its stored value accessed only by an lvalue expression that has one of the
following types: 89)
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective
type of the object,
- an aggregate or union type that includes one of the aforementioned types among its members
(including, recursively, a member of a subaggregate or contained union), or
- a character type.
In this case the object is of type uint8_t
and type of the lvalue expression is Message
. None of the exceptions above apply.
Using memcpy
over dereferences fixes this issue, or alternatively you can disable the strict aliasing in your compiler if you want to write in non-C language.
But even after this the code has a lot of problems and is not portable: Bitfields are extremely poorly defined in standard and overall structures are very clumsy way to handle serialization. You should take the option of deserializing each member manually.
You are right.
C17 draft § 6.5:
An object shall have its stored value accessed only by an lvalue expression that has one of the
following types: 89)
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective
type of the object,
- an aggregate or union type that includes one of the aforementioned types among its members
(including, recursively, a member of a subaggregate or contained union), or
- a character type.
In this case the object is of type uint8_t
and type of the lvalue expression is Message
. None of the exceptions above apply.
Using memcpy
over dereferences fixes this issue, or alternatively you can disable the strict aliasing in your compiler if you want to write in non-C language.
But even after this the code has a lot of problems and is not portable: Bitfields are extremely poorly defined in standard and overall structures are very clumsy way to handle serialization. You should take the option of deserializing each member manually.
answered Jan 17 at 14:17
user694733user694733
10.9k12850
10.9k12850
1
In all fairness, disabling strict aliasing is not a bad idea at all. In particularly or embedded systems design, which this appears to be. Strict aliasing/effective type is a broken part of the C language, specified by PC programmers who don't grasp the type punning often needed in embedded systems. In addition, GCC is the only compiler I know of which abuses strict aliasing to produce faster code that does not at all perform what the unaware programmer intended. But it does so efficiently! Better to always usegcc -fno-strict-aliasing
.
– Lundin
Jan 17 at 14:22
1
@Lundin But the flip side of that is programmers with no experience on systems where violating strict aliasing causes actual problems. Thus you get questions like this one, where a bunch of programmers with apparently no understanding at all regarding strict aliasing looks to have prompted someone who does understand the implications to post a question on Stackoverflow.
– Andrew Henle
Jan 17 at 14:28
@AndrewHenle But the strict aliasing rule isn't really common knowledge. It takes a hardened C veteran/language nerd to know about it and understand it. Which in turn makes it a safety hazard and language flaw. Particularly since prior C99, no compiler did aggressive optimizations relying on it... and it took around 10 years for C99 to become industry standard. We didn't really see strict aliasing bugs in embedded systems until around 2009, when gcc started to become popular as ARM compiler. And so the non-nerds have to use MISRA-C to tell them "don't do this" without understanding why.
– Lundin
Jan 17 at 15:35
add a comment |
1
In all fairness, disabling strict aliasing is not a bad idea at all. In particularly or embedded systems design, which this appears to be. Strict aliasing/effective type is a broken part of the C language, specified by PC programmers who don't grasp the type punning often needed in embedded systems. In addition, GCC is the only compiler I know of which abuses strict aliasing to produce faster code that does not at all perform what the unaware programmer intended. But it does so efficiently! Better to always usegcc -fno-strict-aliasing
.
– Lundin
Jan 17 at 14:22
1
@Lundin But the flip side of that is programmers with no experience on systems where violating strict aliasing causes actual problems. Thus you get questions like this one, where a bunch of programmers with apparently no understanding at all regarding strict aliasing looks to have prompted someone who does understand the implications to post a question on Stackoverflow.
– Andrew Henle
Jan 17 at 14:28
@AndrewHenle But the strict aliasing rule isn't really common knowledge. It takes a hardened C veteran/language nerd to know about it and understand it. Which in turn makes it a safety hazard and language flaw. Particularly since prior C99, no compiler did aggressive optimizations relying on it... and it took around 10 years for C99 to become industry standard. We didn't really see strict aliasing bugs in embedded systems until around 2009, when gcc started to become popular as ARM compiler. And so the non-nerds have to use MISRA-C to tell them "don't do this" without understanding why.
– Lundin
Jan 17 at 15:35
1
1
In all fairness, disabling strict aliasing is not a bad idea at all. In particularly or embedded systems design, which this appears to be. Strict aliasing/effective type is a broken part of the C language, specified by PC programmers who don't grasp the type punning often needed in embedded systems. In addition, GCC is the only compiler I know of which abuses strict aliasing to produce faster code that does not at all perform what the unaware programmer intended. But it does so efficiently! Better to always use
gcc -fno-strict-aliasing
.– Lundin
Jan 17 at 14:22
In all fairness, disabling strict aliasing is not a bad idea at all. In particularly or embedded systems design, which this appears to be. Strict aliasing/effective type is a broken part of the C language, specified by PC programmers who don't grasp the type punning often needed in embedded systems. In addition, GCC is the only compiler I know of which abuses strict aliasing to produce faster code that does not at all perform what the unaware programmer intended. But it does so efficiently! Better to always use
gcc -fno-strict-aliasing
.– Lundin
Jan 17 at 14:22
1
1
@Lundin But the flip side of that is programmers with no experience on systems where violating strict aliasing causes actual problems. Thus you get questions like this one, where a bunch of programmers with apparently no understanding at all regarding strict aliasing looks to have prompted someone who does understand the implications to post a question on Stackoverflow.
– Andrew Henle
Jan 17 at 14:28
@Lundin But the flip side of that is programmers with no experience on systems where violating strict aliasing causes actual problems. Thus you get questions like this one, where a bunch of programmers with apparently no understanding at all regarding strict aliasing looks to have prompted someone who does understand the implications to post a question on Stackoverflow.
– Andrew Henle
Jan 17 at 14:28
@AndrewHenle But the strict aliasing rule isn't really common knowledge. It takes a hardened C veteran/language nerd to know about it and understand it. Which in turn makes it a safety hazard and language flaw. Particularly since prior C99, no compiler did aggressive optimizations relying on it... and it took around 10 years for C99 to become industry standard. We didn't really see strict aliasing bugs in embedded systems until around 2009, when gcc started to become popular as ARM compiler. And so the non-nerds have to use MISRA-C to tell them "don't do this" without understanding why.
– Lundin
Jan 17 at 15:35
@AndrewHenle But the strict aliasing rule isn't really common knowledge. It takes a hardened C veteran/language nerd to know about it and understand it. Which in turn makes it a safety hazard and language flaw. Particularly since prior C99, no compiler did aggressive optimizations relying on it... and it took around 10 years for C99 to become industry standard. We didn't really see strict aliasing bugs in embedded systems until around 2009, when gcc started to become popular as ARM compiler. And so the non-nerds have to use MISRA-C to tell them "don't do this" without understanding why.
– Lundin
Jan 17 at 15:35
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54237004%2fstrict-aliasing-rule-uint8-t-buffer-to-structure%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Besides the strict aliasing violation problem, beware of indianness. Filling a uint from a char with memcpy makes your code completely non portable.
– Alain Merigot
Jan 17 at 13:48
@AlainMerigot Good catch!, We are handling endianness just before the
memcpy
or pointer assignment, it is just that I have not shown here.– kiran Biradar
Jan 17 at 13:53
I think that strict aliasing is a compiler thing and usually it has a flag if it may or may not assume strict aliasing for different optimization levels.
– Alex Lop.
Jan 17 at 13:58
1
@user694733 I have bit fields defined.
– kiran Biradar
Jan 17 at 14:02
1
Your bitfields add up to 32 bits, in a
uint64_t
. You have no idea where in those 64 bits those bitfields actually are. Per 6.7.2.1p11: "whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified." Bitfields are just plain bad news for portability and maintainability.– Andrew Henle
Jan 17 at 14:21