Search code examples
c++standardsnarrowing

Narrowing conversion in C++


In Beej's Guide to Network Programming, there is a function that was meant to provide a portable way to serialize a 16-bit integer.

/*
** packi16() -- store a 16-bit int into a char buffer (like htons())
*/ 
void packi16(unsigned char *buf, unsigned int i)
{
    *buf++ = i>>8; *buf++ = i;
}

I don't understand why the statement *buf++ = i; is portable, as the assignment of an unsigned integer (i) to an unsigned character (*buf) would result in a narrowing conversion.

  • Does the C++ standard guarantees that in such a conversion, the unsigned int is always truncated and its least significant 8 bits are retained in the unsigned char?
  • If not, is there any preferred way to fix the issue? Is it adequate to change the function body to the following?

    *buf++ = (i>>8) & 0xFFFFU; *buf++ = i & 0xFFFFU;


Solution

  • The code assumes an 8-bit byte, and that is not portable.

    E.g. some Texas Instruments digital signal processors have 16-bit byte.

    The number of bits per byte is given by CHAR_BIT from <limits.h>.

    Also, the code assumes that unsigned is 16 bits, which is not portable.

    In summary the code is not portable.


    Re

    Does the C++ standard guarantees that in such a conversion, the unsigned int is always truncated and its least significant 8 bits are retained in the unsigned char?

    No, since the C++ standard does not guarantee that the number of bits per byte is 8.

    The only guarantee is that it's at least 8 bits.

    Unsigned arithmetic is guaranteed modular, however.


    Re

    ” If not, is there any preferred way to fix the issue?

    Use a simple loop, iterating sizeof(unsigned) times.

    The code in question appears to have been distilled from such a loop, since the post-increment in *buf++ = i; is totally meaningless (this is the last use of buf).