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

Popular posts from this blog

toolbar - How to add link to user registration inside toobar in admin joomla 3 custom component -

linux - disk space limitation when creating war file -