offset = (offset + 3) & ~(decltype(offset))3;
or
#include <type_traits> // ... offset = (offset + 3) & ~static_cast<std::remove_reference<decltype(offset)>::type>(3);
if you need to support the possibility that offset is a reference type, for example size_t& . Thanks @BaummitAugen.
It also works when offset wider than int , even without cast, offset = (offset + 3) & ~3; but I'm not sure if it matches the language standard.
This algorithm can be described in English as "add 3, then round."
The rounding part is done by setting the two least significant bits to zero using binary arithmetic. The bits are reset by applying binary AND with the inverse bit pattern, in other words, we make the bit pattern of all bits whose value we want to keep unchanged and use it as a mask.
Binary representation 3 : 00000011
We get the inverse bitmask with ~ (bitwise NOT, so as not to be confused with the operator ! Logical NOT): ~3
For the ~ operator, the size of the operand determines the size of the result, so on my machine ~(unsigned char)3 == 252 ( 1111 1100 ) and ~(unsigned int)3 == 4294967292 ( 1111 1111 1111 1111 1111 1111 1111 1100 ). The default size for an integer literal is int .
When offset is a wider type (has more bits) than int , we need the operand in ~ expand so that the bitmask matches. That's why we impose literal 3 on the same type as offset .
source share