Unexpected results while bit-shifting in C -
i hit unexpected behavior while porting code. i've boiled down example:
#include <stdint.h> #include <stdio.h> uint32_t swap_16_p(uint8_t *value) { return (*(uint16_t*)value << 8 | *(uint16_t*)value >> 8); } int main() { uint8_t start[] = { 0xde, 0xad, 0xbe, 0xef, 0xbe, 0xef }; printf("0x%08x\n", swap_16_p(start)); return 0; } on little endian system x86-64 expect print 0x0000dead instead prints 0x00addead. looking @ assembly output makes issue more clear:
uint32_t swap_16_p(uint8_t *value) { 400506: 55 push %rbp 400507: 48 89 e5 mov %rsp,%rbp 40050a: 48 89 7d f8 mov %rdi,-0x8(%rbp) return (*(uint16_t*)value << 8 | *(uint16_t*)value >> 8); 40050e: 48 8b 45 f8 mov -0x8(%rbp),%rax 400512: 0f b7 00 movzwl (%rax),%eax 400515: 0f b7 c0 movzwl %ax,%eax 400518: c1 e0 08 shl $0x8,%eax 40051b: 89 c2 mov %eax,%edx 40051d: 48 8b 45 f8 mov -0x8(%rbp),%rax 400521: 0f b7 00 movzwl (%rax),%eax 400524: 66 c1 e8 08 shr $0x8,%ax 400528: 0f b7 c0 movzwl %ax,%eax 40052b: 09 d0 or %edx,%eax } 40052d: 5d pop %rbp 40052e: c3 retq by using eax scratch area doing computation, byte gets shifted past 16-bit boundary shl $0x8,%eax. wouldn't have expected computation treated 32-bit value until before return (as need promote uint32_t); similar behavior seen when storing value in temporary uint32_t , printing instead.
have gone against (or improperly interpreted) c spec, or compiler bug (seems unlikely since happens in both clang , gcc)?
the integer promotions done @ "read side", therefore while expression evaluated. means after reading integer value has smaller size int resp. unsigned converted:
the following may used in expression wherever int or unsigned int may used:
— object or expression integer type integer conversion rank less or equal rank of int , unsigned int.
— bit-field of type _bool, int, signed int, or unsigned int.
if int can represent values of original type, value converted int; otherwise, converted unsigned int. these called integer promotions. 48)
48) integer promotions applied only: part of usual arithmetic conversions, argument expressions, operands of unary +, -, , ~ operators, , both operands of shift operators, specified respective subclauses.
iso/iec 9899:tc3 6.3.1.1-2
therefore
*(uint16_t*)value is converted int , shifted.
Comments
Post a Comment