3 ms·
The first example in the article is flawed (or at least misleading). 1) They define a char array (which defaults to signed char, as mentioned in the post), inc
by themulticaster 5y ago
The first example in the article is flawed (or at least misleading).
1) They define a char array (which defaults to signed char, as mentioned in the post), including the value 0x80 which can't be represented in char, resulting in a compiler warning (e.g. in GCC 11.1).
The mentioned reason against using unsigned char (that shifting 128 left by 24 places results in UB) is also misleading: I could not reproduce the UB when changing the array to unsigned char. Perhaps the author meant leaving the array defined as signed char, but casting the signed chars to unsigned before shifting. That indeed results in UB, but I don't see why you would define the array as signed in the first place.
2) The cause for the undefined behavior isn't the bswap_32, rather it's because they try reading an uint32_t value from a char array, where b[0] is not aligned on a word boundary.
There is no need at all do redefine bswap. The simple solution would be to use an unsigned char array instead of a char array and just reading the values byte-wise.
Of course C has its footguns and warts and so on, but there is no need to dramatize it this much in my opinion.
I've prepared a Godbolt example to better explain the arguments mentioned above: https://godbolt.org/z/Y1EWK6e17 https://godbolt.org/z/Y1EWK6e17
Edit: To add to point 2) above: Another way to avoid the UB (in this specific case) would be to add __attribute__ ((aligned (4))) to the definition of b. In that case, even reading the array as a single uint32_t works as expected since the access is aligned to a word boundary.
Obviously, you can't expect any random (unsigned char) pointer to be aligned on a word boundary. Therefore, it is still necessary to read the uint32_t byte by byte.
- cygx 5y ago> The mentioned reason against using unsigned char (that shifting 128 left by 24 places results in UB) is also misleading No, that reasoning is correct. Integer promotions are performed on the operands of a shift expression, meaning the left operand will be promoted to signed int even if it starts out as unsigned char. Trying to shift a byte value with highest bit set by 24 will results in a value not representable as signed int, leading to UB.
- themulticaster 5y agoThanks, I just noticed a small mistake in my example (I don't trigger the UB because I access b[0] containing 0x80 without shifting, however I meant to do it the other way around). Still, adding an explicit cast to the left operand seems to be enough to avoid this, e.g.: uint32_t x = ((uint32_t)b[0]) << 24; In summary, I think my point that using unsigned char would be appropriate in this case still stands.
- cygx 5y ago> Still, adding an explicit cast to the left operand seems to be enough to avoid this Indeed. See my other comment, https://news.ycombinator.com/item?id=27086482 https://news.ycombinator.com/item?id=27086482